
Authenticate to Soongsil University LMS and fetch terms, courses, todos, attendance, announcements and scores; maintains session cookies/tokens, supports PEM normalization and progress callbacks.
숭실대학교 LMS(Canvas, LearningX)에서 학기, 강의, 할 일, 출석, 공지, 점수 정보를 가져오기 위한 Kotlin Multiplatform 라이브러리입니다.
현재 코드 기준 지원 타깃은 다음과 같습니다.
iosX64, iosArm64, iosSimulatorArm64)library/build.gradle.kts 기준 publish 좌표는 아래와 같습니다.
io.github.chlwhdtn03
lms
1.1.3
프로젝트에 mavenCentral()이 포함되어 있어야 합니다.
repositories {
mavenCentral()
google()
}KMP 프로젝트라면 보통 commonMain에 추가하면 됩니다.
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.chlwhdtn03:lms:1.1.3")
}
}
}Android 전용 모듈에서만 사용할 경우에는 일반 dependencies 블록에서도 사용할 수 있습니다.
dependencies {
implementation("io.github.chlwhdtn03:lms:1.1.3")
}이 저장소처럼 :library 모듈을 직접 포함해서 사용할 수도 있습니다.
dependencies {
implementation(project(":library"))
}이 라이브러리의 사용 순서는 고정되어 있습니다.
loginLMS(id, password)로 로그인getTerms()로 학기 목록 조회getSubjects(term)로 특정 학기의 강의 정보 조회getTerms()와 getSubjects()는 로그인 전에 호출하면 IllegalStateException이 발생합니다.
또한 공개 API는 모두 네트워크 호출을 포함하므로 suspend 함수이며, 코루틴 안에서 호출해야 합니다.
일반적인 앱 코드에서는 사실상 loginLMS, getTerms, getSubjects 세 함수만 사용하면 됩니다.
suspend fun loginLMS(id: String, password: String): BooleanLMS 아이디와 비밀번호로 로그인합니다.
true를 반환합니다.IllegalArgumentException이 발생할 수 있습니다.getTerms()와 getSubjects()가 같은 세션을 사용합니다.@OptIn(ExperimentalTime::class)
suspend fun getTerms(): List<Term>로그인한 사용자의 학기 목록을 가져옵니다.
Term의 핵심 필드는 다음과 같습니다.
id: 학기 IDname: 학기명start_at: 시작 시각end_at: 종료 시각@OptIn(ExperimentalTime::class)
suspend fun getSubjects(
term: Term,
loadingState: (Float) -> Unit = {}
): List<Subject>선택한 학기의 강의 목록을 가져옵니다. 한 강의 안에 아래 정보가 함께 들어 있습니다.
loadingState 콜백에는 0.0f ~ 1.0f 범위의 진행률이 전달됩니다.
fun normalizePem(raw: String): String로그인 복호화 과정에서 PEM 문자열 형식을 정리하는 보조 함수입니다.
expect fun pemToString(rawPem: String, rawPw: String): String플랫폼별 RSA 복호화를 수행하는 내부용 함수입니다.
actual 구현이 제공됩니다.loginLMS() 내부에서 자동으로 사용되므로 직접 호출하지 않아도 됩니다.getSubjects()가 반환하는 핵심 모델입니다.
id: 과목 IDtermId: 학기 IDtermName: 학기명name: 과목명professor: 교수명totalStudents: 수강 인원todoList: 할 일 목록attendances: 주차별 출석 정보discussions: 공지 목록scoredAssignments: 과제/시험 점수 목록할 일 한 건을 의미합니다.
component_type: 항목 타입, 예: assignment, commons
assignment_id: 과제 IDtitle: 제목due_date: 마감 시각출석 상태는 enum 으로 제공됩니다.
AttendanceType.ATTENDANCEAttendanceType.ABSENTAttendanceType.LATEAttendanceType.NONE한글 값은 kor 프로퍼티로 확인할 수 있습니다.
val text = AttendanceType.ATTENDANCE.kor // "출석"점수 정보는 아래 형태로 들어옵니다.
groupName: 평가 항목 그룹명, 예: 과제 / 퀴즈 / 기말고사name: 과제 또는 시험 이름score: 내가 받은 점수maxScore: 만점아래 예제는 로그인부터 학기 선택, 과목 조회, 과제/공지/점수 출력까지 한 번에 보여줍니다.
import io.github.chlwhdtn03.getSubjects
import io.github.chlwhdtn03.getTerms
import io.github.chlwhdtn03.loginLMS
import io.github.chlwhdtn03.data.AttendanceType
import kotlin.time.ExperimentalTime
@OptIn(ExperimentalTime::class)
suspend fun loadLmsExample() {
val loginSuccess = loginLMS(
id = "20222908",
password = "비밀번호"
)
if (!loginSuccess) return
val terms = getTerms()
val selectedTerm = terms.lastOrNull()
?: error("조회 가능한 학기가 없습니다.")
println("선택한 학기: ${selectedTerm.name}")
val subjects = getSubjects(selectedTerm) { progress ->
println("불러오는 중: ${(progress * 100).toInt()}%")
}
subjects.forEach { subject ->
println("과목명: ${subject.name}")
println("교수명: ${subject.professor}")
println("수강인원: ${subject.totalStudents}")
println("[할 일]")
subject.todoList.forEach { todo ->
println("- ${todo.title} / ${todo.component_type} / 마감: ${todo.due_date}")
}
println("[출석]")
subject.attendances.forEachIndexed { weekIndex, weekAttendances ->
val attendanceText = weekAttendances.joinToString { it.kor }
println("- ${weekIndex + 1}주차: $attendanceText")
}
println("[공지]")
subject.discussions.forEach { discussion ->
println("- ${discussion.title} / 작성자: ${discussion.user_name}")
}
println("[점수]")
subject.scoredAssignments.forEach { score ->
println("- [${score.groupName}] ${score.name}: ${score.score}/${score.maxScore}")
}
println()
}
}Android 앱에서 사용할 때는 보통 viewModelScope.launch 안에서 호출하면 됩니다.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.github.chlwhdtn03.getSubjects
import io.github.chlwhdtn03.getTerms
import io.github.chlwhdtn03.loginLMS
import kotlinx.coroutines.launch
import kotlin.time.ExperimentalTime
class LmsViewModel : ViewModel() {
@OptIn(ExperimentalTime::class)
fun load(id: String, password: String) {
viewModelScope.launch {
loginLMS(id, password)
val term = getTerms().lastOrNull() ?: return@launch
val subjects = getSubjects(term) { progress ->
println("progress = $progress")
}
subjects.forEach {
println(it.name)
}
}
}
}getTerms()와 getSubjects()는 반드시 로그인 이후에 호출해야 합니다.Term과 관련 함수는 kotlin.time.ExperimentalTime opt-in 이 필요합니다.실제 사용에서는 아래 흐름만 기억하면 됩니다.
@OptIn(ExperimentalTime::class)
suspend fun simpleFlow() {
loginLMS("학번", "비밀번호")
val term = getTerms().last()
val subjects = getSubjects(term)
println(subjects.map { it.name })
}숭실대학교 LMS(Canvas, LearningX)에서 학기, 강의, 할 일, 출석, 공지, 점수 정보를 가져오기 위한 Kotlin Multiplatform 라이브러리입니다.
현재 코드 기준 지원 타깃은 다음과 같습니다.
iosX64, iosArm64, iosSimulatorArm64)library/build.gradle.kts 기준 publish 좌표는 아래와 같습니다.
io.github.chlwhdtn03
lms
1.1.3
프로젝트에 mavenCentral()이 포함되어 있어야 합니다.
repositories {
mavenCentral()
google()
}KMP 프로젝트라면 보통 commonMain에 추가하면 됩니다.
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.chlwhdtn03:lms:1.1.3")
}
}
}Android 전용 모듈에서만 사용할 경우에는 일반 dependencies 블록에서도 사용할 수 있습니다.
dependencies {
implementation("io.github.chlwhdtn03:lms:1.1.3")
}이 저장소처럼 :library 모듈을 직접 포함해서 사용할 수도 있습니다.
dependencies {
implementation(project(":library"))
}이 라이브러리의 사용 순서는 고정되어 있습니다.
loginLMS(id, password)로 로그인getTerms()로 학기 목록 조회getSubjects(term)로 특정 학기의 강의 정보 조회getTerms()와 getSubjects()는 로그인 전에 호출하면 IllegalStateException이 발생합니다.
또한 공개 API는 모두 네트워크 호출을 포함하므로 suspend 함수이며, 코루틴 안에서 호출해야 합니다.
일반적인 앱 코드에서는 사실상 loginLMS, getTerms, getSubjects 세 함수만 사용하면 됩니다.
suspend fun loginLMS(id: String, password: String): BooleanLMS 아이디와 비밀번호로 로그인합니다.
true를 반환합니다.IllegalArgumentException이 발생할 수 있습니다.getTerms()와 getSubjects()가 같은 세션을 사용합니다.@OptIn(ExperimentalTime::class)
suspend fun getTerms(): List<Term>로그인한 사용자의 학기 목록을 가져옵니다.
Term의 핵심 필드는 다음과 같습니다.
id: 학기 IDname: 학기명start_at: 시작 시각end_at: 종료 시각@OptIn(ExperimentalTime::class)
suspend fun getSubjects(
term: Term,
loadingState: (Float) -> Unit = {}
): List<Subject>선택한 학기의 강의 목록을 가져옵니다. 한 강의 안에 아래 정보가 함께 들어 있습니다.
loadingState 콜백에는 0.0f ~ 1.0f 범위의 진행률이 전달됩니다.
fun normalizePem(raw: String): String로그인 복호화 과정에서 PEM 문자열 형식을 정리하는 보조 함수입니다.
expect fun pemToString(rawPem: String, rawPw: String): String플랫폼별 RSA 복호화를 수행하는 내부용 함수입니다.
actual 구현이 제공됩니다.loginLMS() 내부에서 자동으로 사용되므로 직접 호출하지 않아도 됩니다.getSubjects()가 반환하는 핵심 모델입니다.
id: 과목 IDtermId: 학기 IDtermName: 학기명name: 과목명professor: 교수명totalStudents: 수강 인원todoList: 할 일 목록attendances: 주차별 출석 정보discussions: 공지 목록scoredAssignments: 과제/시험 점수 목록할 일 한 건을 의미합니다.
component_type: 항목 타입, 예: assignment, commons
assignment_id: 과제 IDtitle: 제목due_date: 마감 시각출석 상태는 enum 으로 제공됩니다.
AttendanceType.ATTENDANCEAttendanceType.ABSENTAttendanceType.LATEAttendanceType.NONE한글 값은 kor 프로퍼티로 확인할 수 있습니다.
val text = AttendanceType.ATTENDANCE.kor // "출석"점수 정보는 아래 형태로 들어옵니다.
groupName: 평가 항목 그룹명, 예: 과제 / 퀴즈 / 기말고사name: 과제 또는 시험 이름score: 내가 받은 점수maxScore: 만점아래 예제는 로그인부터 학기 선택, 과목 조회, 과제/공지/점수 출력까지 한 번에 보여줍니다.
import io.github.chlwhdtn03.getSubjects
import io.github.chlwhdtn03.getTerms
import io.github.chlwhdtn03.loginLMS
import io.github.chlwhdtn03.data.AttendanceType
import kotlin.time.ExperimentalTime
@OptIn(ExperimentalTime::class)
suspend fun loadLmsExample() {
val loginSuccess = loginLMS(
id = "20222908",
password = "비밀번호"
)
if (!loginSuccess) return
val terms = getTerms()
val selectedTerm = terms.lastOrNull()
?: error("조회 가능한 학기가 없습니다.")
println("선택한 학기: ${selectedTerm.name}")
val subjects = getSubjects(selectedTerm) { progress ->
println("불러오는 중: ${(progress * 100).toInt()}%")
}
subjects.forEach { subject ->
println("과목명: ${subject.name}")
println("교수명: ${subject.professor}")
println("수강인원: ${subject.totalStudents}")
println("[할 일]")
subject.todoList.forEach { todo ->
println("- ${todo.title} / ${todo.component_type} / 마감: ${todo.due_date}")
}
println("[출석]")
subject.attendances.forEachIndexed { weekIndex, weekAttendances ->
val attendanceText = weekAttendances.joinToString { it.kor }
println("- ${weekIndex + 1}주차: $attendanceText")
}
println("[공지]")
subject.discussions.forEach { discussion ->
println("- ${discussion.title} / 작성자: ${discussion.user_name}")
}
println("[점수]")
subject.scoredAssignments.forEach { score ->
println("- [${score.groupName}] ${score.name}: ${score.score}/${score.maxScore}")
}
println()
}
}Android 앱에서 사용할 때는 보통 viewModelScope.launch 안에서 호출하면 됩니다.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.github.chlwhdtn03.getSubjects
import io.github.chlwhdtn03.getTerms
import io.github.chlwhdtn03.loginLMS
import kotlinx.coroutines.launch
import kotlin.time.ExperimentalTime
class LmsViewModel : ViewModel() {
@OptIn(ExperimentalTime::class)
fun load(id: String, password: String) {
viewModelScope.launch {
loginLMS(id, password)
val term = getTerms().lastOrNull() ?: return@launch
val subjects = getSubjects(term) { progress ->
println("progress = $progress")
}
subjects.forEach {
println(it.name)
}
}
}
}getTerms()와 getSubjects()는 반드시 로그인 이후에 호출해야 합니다.Term과 관련 함수는 kotlin.time.ExperimentalTime opt-in 이 필요합니다.실제 사용에서는 아래 흐름만 기억하면 됩니다.
@OptIn(ExperimentalTime::class)
suspend fun simpleFlow() {
loginLMS("학번", "비밀번호")
val term = getTerms().last()
val subjects = getSubjects(term)
println(subjects.map { it.name })
}