강의 정보 갱신 AI 자동화
KOIN 서비스의 강의 시간표 최신화를 AI로 자동화한 기록
KOIN 서비스에서는 버스 시간표, 생활협동조합(편의점, 학생식당 등) 운영 정보, 강의 정보 등 학교생활에 필요한 다양한 정보를 제공하고 있습니다.
이 정보들은 주기적으로 변경되기 때문에 최신 상태를 유지하기 위한 업데이트 작업이 필요했습니다. 기존에는 백엔드 개발자가 직접 업데이트를 진행했으며, 도메인마다 관리 방식도 달랐습니다.
- 버스 시간표, 생활협동조합 시간표: 정해진 양식에 맞춰 엑셀 파일을 작성한 뒤 백오피스에 업로드하고, 필요한 추가 정보를 DB에 직접 입력
- 강의 정보: 서버에 강의 엑셀 파일을 업로드하고, 해당 엑셀 양식에 맞는 Python 파싱 코드를 작성해 업데이트
과거에는 버스와 생활협동조합 정보 역시 강의 정보와 비슷한 방식으로 관리했습니다. 하지만 매번 코드를 수정하거나 DB에 직접 데이터를 입력하는 방식은 안정성과 유지보수 측면에서 문제가 있었고, 이후 백오피스에서 관리하는 방식으로 개선했습니다.
그럼에도 여전히 문제가 남아 있었습니다.
2026년 여름방학으로 넘어가는 시점에는 버스, 생활협동조합, 강의 정보가 모두 한 달 가까이 갱신되지 않았고, 실제 사용자로부터 정보가 오래되었다는 불만이 접수되기도 했습니다.

겉으로만 보면 단순히 담당자가 업데이트를 제때 하지 않은 문제로 볼 수도 있습니다. 하지만 원인을 살펴보니 특정 팀이나 담당자의 실수보다는 정보 업데이트 과정 자체가 백엔드 개발자에게 의존하고 있고, 한 번의 업데이트에도 많은 작업이 필요하다는 구조적인 문제가 더 크다고 판단했습니다.
예를 들어 백오피스에서 엑셀 파일을 업로드하는 방식이라 하더라도 아무 엑셀 파일이나 사용할 수 있는 것은 아니었습니다. 정해진 양식에 맞춰 데이터를 다시 작성해야 했고, 그 양식을 정확히 파악하기 위해 별도의 문서를 확인하거나 학교에서 제공한 시간표를 다시 옮겨 적는 작업도 필요했습니다.
결과적으로 단순한 데이터 갱신 작업임에도 개발자의 시간과 반복적인 수작업이 많이 필요했습니다.
이 문제를 해결하기 위해 기존 데이터 갱신 흐름을 잘 알고 있는 백엔드 개발자를 중심으로 3명의 팀원이 자동화 TF를 구성했습니다.
자동화 프로세스
자동화 TF에서는 버스, 생활협동조합, 강의 정보 세 도메인을 한 명씩 맡아 자동화를 진행했고, 저는 그중 강의 정보 도메인을 담당했습니다.
강의 정보는 정규학기 또는 계절학기 시작 전에 학교 학사공지에 엑셀 파일 형태로 업로드됩니다.
문제는 학교에서 제공하는 엑셀 파일의 양식이 일정하지 않다는 점이었습니다.
class ColumnNames(Enum):
# 매핑이 되는 칼럼 명을 여러가지로 설정할 수 있도록 하였다.
# `학\n점` 등으로 적혀져 있는 것을 주의할 것
# 23.2 기준, 학교에서 "수강정원" 대신 "수정정원"으로 올려놓아서, 이에 대응하기 위해 배열로 여러 후보군을 설정하였다.
# 25.1 기준, No. 행의 값이 없는 경우 생략하도록 추가
ID = ["No."]
SEMESTER = ["학기"]
CODE = ["과목코드"]
NAME = ["교과목명"]
GRADES = ["학\n점", "학점"]
CLASS_NUMBER = ["분반"]
REGULAR_NUMBER = ["수강\n정원", "수정\n정원", "정원"]
DEPARTMENT = ["개설학부(과)"]
TARGET = ["수강신청\n가능학년", "수강대상 (학부/전공/학년)", "대상학부(과)"]
PROFESSOR = ["담당교수"]
IS_ENGLISH = ["영어강의"]
DESIGN_SCORE = ["설\n계", "설계"]
IS_ELEARNING = ["E-Learning"]
CLASS_TIME = ["강의시간"]
실제로 기존에 사용하던 Python 코드에서도 하나의 데이터를 여러 칼럼명과 매칭하도록 처리하고 있었습니다. 학점이 학\n점으로 작성되거나, 수강정원이 특정 학기에는 수정정원으로 작성되는 등 학기마다 표기 방식과 엑셀 구조가 계속 달라졌기 때문입니다.
이러한 상황에서는 특정 칼럼명을 기준으로 단순 매칭하는 방식만으로 안정적인 자동화를 만들기 어렵다고 판단했습니다. 새로운 형식이 등장할 때마다 파싱 로직을 수정해야 한다면 자동화를 도입하더라도 결국 지속적인 개발자의 개입이 필요하기 때문입니다.
따라서 처음에는 고정된 규칙만으로 데이터를 파싱하는 대신, AI가 엑셀의 구조와 각 칼럼의 의미를 해석해 강의 업데이트 API가 요구하는 형태로 직접 변환하도록 구성했습니다.
다만 AI가 데이터를 변환한다고 해서 결과를 곧바로 반영하지는 않았습니다. 강의 정보는 많은 사용자가 실제 수강 정보를 확인하는 데 사용하는 데이터인 만큼, 잘못된 변환 결과가 그대로 반영되는 것을 막기 위해 AI가 변환을 담당하고 사람이 최종 결과를 검수하는 Human-in-the-loop 방식으로 프로세스를 구성했습니다.
전체 흐름은 아래와 같습니다.
[학사 공지 감지]
│
▼
[Slack 알림 전송]
│
▼
업데이트 진행?
┌──┴──┐
아니요 예
│ │
[종료] ▼
[Excel 데이터 변환]
│
▼
[검수 HTML 생성]
│
▼
[Slack 링크 전송]
│
▼
변환 결과 확인
┌──┴───────┐
이상 있음 정상
│ │
▼ │
[Slack 수정 요청] │
│ │
▼ │
[수정 반영] │
│ │
└────┐ ┌───┘
▼ ▼
[최종 승인]
│
▼
[강의 업데이트 API]
│
▼
[반영 완료]
기존에는 개발자가 직접 엑셀 구조를 확인하고 파싱 코드를 수정한 뒤 업데이트까지 진행해야 했다면, 자동화 이후에는 공지 감지 → 데이터 변환 → 결과 검수 → 업데이트 과정이 하나의 흐름으로 연결됩니다.
AI로 전체를 변환
처음에는 엑셀 파일 자체를 AI에게 전달하고 API에 맞는 형식으로 데이터를 변환하도록 구현했습니다.
엑셀 양식이 학기마다 달라졌기 때문에 사람이 새로운 양식에 맞춰 파싱 코드를 계속 수정하는 대신, AI가 엑셀의 구조를 이해하고 바로 필요한 형태로 변환해준다면 가장 단순하게 문제를 해결할 수 있다고 생각했습니다.
하지만 생각과 다르게 몇 가지 문제를 겪었습니다.
가장 큰 문제는 토큰 사용량이었습니다.
강의 엑셀에는 수백개의 강의 정보가 포함되어 있었고 이를 AI에게 전달하면 입력 데이터 자체가 매우 커졌으며 변환된 전체 강의 데이터를 다시 응답으로 받으면서 한 번의 변환에도 많은 토큰이 들었습니다.
다음으로 문제가 발생했을 때 원인을 파악하기 어렵다는 점이 있었습니다.
예를 들어 특정 강의의 수강정원이나 강의 시간이 잘못 변환되더라도 그 원인이 엑셀 구조를 잘못 이해한 건지, 특정 셀의 값을 잘못 읽은 건지, API 형식에 맞추는 과정에서 문제가 발생한건지 구분하기 어려웠습니다.
무엇보다 비결정적인 AI가 수백개의 강의 데이터를 생성하게 되면 일부 값이 잘못 생성될 가능성을 완전히 배제하기는 어려웠습니다.
이러한 문제들 때문에 전체 변환을 AI에게 맡기는 대신, AI가 잘하는 부분과 코드가 잘하는 부분을 나누는 방향으로 구조를 변경했습니다.
AI는 구조만 파악하도록
다시 한번 구조를 검토해보니 실제로 매번 달라지는 부분은 전체 데이터가 아니라 각 데이터가 어느 컬럼에 위치하는지였습니다.
예를 들어 어떤 학기에는 아래와 같은 컬럼이 사용됐습니다.
학점 수강정원 개설학부(과) 담당교수 강의시간
그런데 다른 학기에는 의미는 같지만 다른 표기로 작성되기도 했습니다.
학 점 수정정원 대상학부(과) 담당교수 강의시간
다시 생각해보면 AI가 필요한 부분은 컬럼의 의미를 알아내는 것까지였습니다. 어떤 컬럼에서 값을 가져와야 하는지만 알 수 있다면 이후 데이터를 API 형식으로 변환하는 작업은 코드로 충분히 처리할 수 있었습니다. 그래서 최종적으로는 아래처럼 역할을 재분배했습니다.
Excel 파일
│
▼
[헤더 + 일부 데이터 추출]
│
▼
[AI: 컬럼 의미 판단]
│
▼
[MappingSpec 생성]
│
▼
[코드: 컬럼 위치 탐색]
│
▼
[행 파싱 / 시간 변환 / 값 검증]
│
▼
[강의 업데이트 API 요청 데이터]
AI는 엑셀 전체 데이터를 다시 생성하지 않고, 각 필드가 어떤 컬럼과 연결되는지만 판단합니다. 이후 그 결과를 바탕으로 코드를 통해 변환하도록 바뀌었습니다.
이렇게 역할을 나누면서 토큰 소모량도 크게 줄일 수 있었고, 문제가 발생했을 때도 어느 단계에서 문제가 생겼는지 확인하기 쉬워졌습니다.
| 방식 | 15회 실행 비용 | 1회 평균 비용 | 비용 차이 |
|---|---|---|---|
| 엑셀 전체 AI 변환 | 약 $7.50 | 약 $0.50 | 약 12.5배 |
| 컬럼 매핑만 AI 사용 | 약 $0.60 | 약 $0.04 | 기준 |
컬럼 위치를 찾게 했을 때의 문제
컬럼 매핑을 시작하고 처음에는 AI에게 각 데이터가 몇 번째 컬럼에 있는지 직접 반환하도록 했습니다.
예를 들면 다음과 같이
과목코드: 3
교과목명: 5
학점: 8
담당교수: 12
하지만 같은 엑셀을 입력해도 실행 때마다 컬럼 번호가 달라지는 문제가 발생했습니다.
원인을 찾아보니 AI에게 전달되는 엑셀 미리보기에서 빈 셀이 생략되면서 모델이 실제 엑셀의 컬럼 번호가 아니라 화면에 보이는 값들을 기준으로 다시 번호를 세고 있었습니다.
셀마다 컬럼 번호를 함께 표시하는 방법도 시도했지만 결과는 오히려 더 불안해졌습니다.
결국 모델에게 숫자를 세는 일을 시키지 않고 각 필드가 어떤 헤더 이름에 해당하는지만 반환하도록 변경하고 실제 컬럼위치를 찾는 것은 코드가 담당하도록 했습니다.
AI의 결과 재검증
AI의 역할을 컬럼 매핑으로 줄였지만 반환된 결과를 그대로 사용하지는 않았습니다.
예를 들어 AI가 개설학부(과) 컬럼을 과목코드로 잘못 판단하거나, 실습 컬럼을 설계 학점으로 판단하는 경우가 있었습니다.
따라서 생성된 매핑을 여러 단계로 검증했습니다.
[AI 컬럼 매핑 생성]
│
▼
[구조 검증]
│
▼
[값 형태 검증]
│
▼
[헤더 기반 반증]
│
▼
[실제 데이터 변환]
│
▼
[변환 결과 검증]
│
┌────┴────┐
실패 성공
│ │
▼ ▼
[실패 원인] [검수 단계]
│
▼
[AI 재요청]
│
└── 최대 횟수까지 반복
검증에 실패하면 실패한 이유를 함께 전달해 다시 매핑을 생성하도록 했습니다. 이 과정은 최대 3회까지만 반복하고, 그 안에 유효한 매핑을 만들지 못하면 자동으로 넘어가지 않고 사용자에게 실패 사실을 알리도록 했습니다.
다만 AI가 정답을 만들 때까지 무한히 재시도하지 않고 최대 횟수를 제한했습니다. 정해진 횟수 안에 올바른 매핑을 만들지 못하면 실패 사실을 사용자에게 그대로 알려 사람이 확인할 수 있도록 했습니다.
이 과정에서 자동화의 방향도 조금 달라졌습니다.
처음에는 “AI가 데이터를 대신 변환해준다”에 가까웠다면, 최종적으로는 “AI는 고정된 규칙으로 처리하기 어려운 부분을 판단하고, 실제 데이터 처리는 검증 가능한 코드가 담당한다”는 구조가 되었습니다.
결과
배치 작업이 새로운 강의 관련 학사 공지를 감지하면 Slack으로 업데이트 여부를 확인하는 알림이 전송됩니다.

업데이트를 진행하면 공지에 첨부된 엑셀 파일을 가져와 컬럼 매핑과 데이터 변환을 수행합니다. 변환이 완료되면 결과를 확인할 수 있는 HTML 링크와 함께 검수 요청 메시지가 전송됩니다.

HTML 페이지에서는 실제 반영될 강의 데이터를 미리 확인할 수 있습니다. 잘못 변환된 값이 있다면 Slack에서 수정 요청을 보내고, 문제가 없다면 최종 반영을 승인합니다.

최종 승인이 완료되면 강의 업데이트 API를 호출해 데이터를 반영하고, 작업 결과를 다시 Slack으로 전달합니다.

자동화 전후의 작업 흐름을 비교하면 아래와 같습니다.
| 구분 | 기존 | 자동화 이후 |
|---|---|---|
| 공지 확인 | 개발자가 직접 확인 | Batch가 자동 감지 |
| 엑셀 구조 확인 | 개발자가 직접 확인 | AI가 컬럼 매핑 생성 |
| 데이터 변환 | 엑셀 형식에 맞춰 코드 수정 | 공통 변환 코드 사용 |
| 변환 결과 확인 | 반영 과정에서 직접 확인 | HTML에서 사전 검수 |
| 오류 수정 | 코드 또는 원본 데이터 수정 | Slack에서 수정 요청 |
| 실제 반영 | 개발자가 직접 실행 | Slack에서 승인 후 자동 반영 |
이번 자동화를 진행하면서 AI에게 많은 작업을 맡기는 것이 반드시 좋은 자동화는 아니라는 점을 느꼈습니다.
처음에는 전체 데이터 변환을 맡겼지만, 실제로 운영 가능한 구조를 만들기 위해서는 AI가 필요한 부분과 코드로 보장해야 하는 부분을 명확하게 나누는 것이 더 중요했으며 결과적으로 개발자가 매 학기 엑셀 형식을 확인하고 파싱 코드를 수정하던 작업을 줄이면서도, 실제 반영 전에는 사람이 결과를 확인할 수 있는 흐름을 만들 수 있었습니다.