CSV 일정표 캘린더 일괄 등록 — 행이 빠지는 이유

엑셀이나 구글시트에 정리해 둔 수십 개의 일정을 캘린더 앱에 하나하나 옮겨 적어 본 적이 있다면 그 번거로움을 아실 겁니다. 결론부터 말하면, 여러 일정이 담긴 CSV 표는 .ics 파일 안에 일정 수만큼의 VEVENT 블록을 나란히 넣으면 한 번에 캘린더로 옮길 수 있습니다.
문제는 변환 과정에서 열 이름이나 날짜 형식이 정해진 규칙과 어긋나면, 화면에 별다른 안내 없이 그 행만 조용히 빠진다는 점입니다. 이 글은 어떤 규칙으로 행이 인식되고 어떤 경우에 빠지는지를 실제 동작 기준으로 정리합니다.
요약 ① 여러 일정 =
.ics하나에VEVENT블록 여러 개. 블록마다UID가 달라야 서로 다른 일정으로 인식됩니다. ② 필수 열은 제목·시작 두 가지뿐이고, 정해진 별칭만 인식됩니다. 하나도 없으면 파일 전체가 변환되지 않습니다. ③ 날짜는 연-월-일에-·/·.구분자만 인식되고, 종료 열을 비우면 시작 값 기준으로 자동 계산됩니다.
여러 일정을 하나씩 옮겨 적는 번거로움
워크숍 일정표, 수업 시간표, 여러 지점 방문 일정처럼 표로 정리된 일정을 캘린더 앱에 넣으려면 보통 이벤트 하나마다 제목·날짜·시간·장소를 따로 입력해야 합니다. 행이 열 개, 스무 개로 늘어나면 이 입력 자체가 상당한 시간이 걸립니다.
iCalendar 표준(RFC 5545)은 BEGIN:VCALENDAR 하나 안에 BEGIN:VEVENT 블록을 여러 개 넣는 것을 허용합니다. 즉 표의 행 하나를 VEVENT 블록 하나로 바꿔 죽 이어 붙이면, 이론적으로는 여러 일정이 담긴 캘린더 파일 하나를 만들 수 있습니다.
손으로 여러 일정을 .ics 하나에 담는 방법
방법 자체는 단순합니다. BEGIN:VCALENDAR로 연 뒤 표의 행마다 BEGIN:VEVENT부터 END:VEVENT까지 블록을 하나씩 추가하고, 마지막에 END:VCALENDAR로 닫으면 됩니다. 각 블록에는 SUMMARY(제목), DTSTART·DTEND(시작·종료)를 채우고 필요하면 LOCATION·DESCRIPTION을 더합니다.
여기서 놓치기 쉬운 조건이 하나 있습니다. 각 VEVENT는 고유한 UID가 있어야 합니다. 블록을 복사해서 값만 바꾸는 방식으로 만들다 보면 UID를 그대로 복사해 오는 경우가 흔한데, 많은 캘린더 앱이 같은 UID를 같은 일정의 갱신으로 처리하기 때문에 일부 일정이 사라진 것처럼 보일 수 있습니다.
행이 서너 개면 이 작업이 어렵지 않지만, 열 개가 넘어가면 UID를 하나씩 새로 만들고 날짜 형식을 통일하는 일이 표를 다시 손으로 옮겨 적는 것과 다르지 않습니다.
저희 도구로 확인하기. 표의 행마다 UID를 새로 만들고 VEVENT 블록으로 바꾸는 과정을, CSV 원본을 그대로 붙여 넣는 것만으로 대신 처리합니다.
무료 · 가입 불필요 · 변환은 브라우저 안에서만 처리되고 서버로 전송되지 않습니다

자주 막히는 지점
| 상황 | 원인 |
|---|---|
| 파일 전체가 변환되지 않고 오류 문구만 뜬다 | 헤더에 '제목'을 뜻하는 열(title·제목·subject·summary·일정)이나 '시작'을 뜻하는 열(start·시작·시작일 등)이 하나도 없는 경우입니다 |
| 특정 행만 조용히 빠진다 | 그 행의 제목 칸이 비어 있거나, 시작 칸의 날짜를 인식하지 못한 경우입니다. 빠진 행 수는 화면에 'N행 건너뜀'으로 표시됩니다 |
| 날짜를 분명히 입력했는데도 인식되지 않는다 | 연-월-일 순서에 -·/·. 구분자만 인식됩니다. '2026년 7월 15일'처럼 한글이 섞이거나 월-일-연도 순서면 인식되지 않습니다 |
| 종료 열을 비웠다 | 시각이 있는 일정은 시작 1시간 뒤, 종일 일정은 시작 다음 날이 자동으로 종료로 채워집니다 |
| 종일 일정의 종료 날짜가 하루 더 늘어난 것처럼 보인다 | 종료 열에 적은 날짜가 일정의 마지막 날이 되도록, 그 다음 날을 DTEND로 계산하기 때문입니다(iCalendar에서 DTEND는 포함되지 않는 배타적 끝입니다) |
표 헤더에 별칭 목록에 없는 이름(예: '항목명', '일자')을 썼다면 그 열은 인식되지 않습니다. 첫 행의 열 이름만 title·start가 포함된 표현으로 바꾸면 나머지 값은 그대로 다시 변환됩니다.
변환은 CSV 파싱부터 .ics 생성까지 전부 브라우저 안에서 처리되고, 입력한 일정 내용은 서버로 전송되거나 저장되지 않습니다. 시간대는 별도 지정 없이 기기의 현지 시각(플로팅 타임)으로 저장됩니다.
정리
- 여러 일정을 옮길 때는 표의 행 하나를
VEVENT블록 하나로 바꾸고, 블록마다UID를 다르게 주면 됩니다. - 필수 열은 제목과 시작 두 가지뿐이며, 정해진 별칭(예: 제목·subject, 시작·start date)만 인식됩니다.
- 날짜는 연-월-일 순서에
-·/·.구분자만 인식되고, 시각이 없으면 종일 일정으로 처리됩니다. - 종료 열을 비우면 시각 일정은 1시간 뒤, 종일 일정은 다음 날이 자동으로 채워집니다.
- 변환은 브라우저 안에서만 일어나고 입력한 일정 데이터는 서버로 전송되지 않습니다.
무료 · 가입 불필요 · 변환은 브라우저 안에서만 처리되고 서버로 전송되지 않습니다
자주 묻는 질문
열 이름을 꼭 title, start처럼 영어로 써야 하나요?
아닙니다. 제목 열은 title 외에 제목·subject·summary·일정 중 하나로 써도 인식되고, 시작 열은 start 외에 시작·시작일·시작시간·start date로 써도 인식됩니다. 대소문자와 앞뒤 공백은 구분하지 않습니다. 이 목록에 없는 이름을 쓰면 그 열은 인식되지 않습니다.
종료 열을 비워두면 시간이 어떻게 채워지나요?
시작 값에 시각이 있는 일정은 종료 열이 비어 있으면 시작 시각의 1시간 뒤가 자동으로 들어갑니다. 시각 없이 날짜만 있는 종일 일정은 시작일 다음 날이 종료로 들어가 하루짜리 일정이 됩니다.
표에 서른 행을 넣었는데 결과에는 스무 개뿐이라고 나옵니다. 왜 그런가요?
제목이나 시작 값이 비어 있거나 날짜 형식을 인식하지 못한 행은 변환에서 빠지고, 그 수가 화면에 'N행 건너뜀'으로 표시됩니다. 완전히 빈 행(모든 칸이 공백)은 이 집계에 포함되지 않으므로, 원본 행 수와 정확히 맞지 않을 수 있습니다. 빠진 행을 찾으려면 제목·시작 칸이 채워져 있는지와 날짜가 연-월-일 형식인지부터 확인하면 됩니다.
변환한 일정 데이터가 서버로 전송되나요?
아닙니다. CSV 파싱과 .ics 생성, 그 반대 방향 변환까지 전부 브라우저 안에서 자바스크립트로 처리되며 입력한 내용은 서버로 전송되거나 저장되지 않습니다. 파일을 올려도 그 파일은 브라우저 메모리 안에서만 읽힙니다.
