Step 109. 심볼릭 링크와 하드링크 — 파일의 또 다른 이름

Step 109. 심볼릭 링크와 하드링크 — 파일의 또 다른 이름

Level 2 — 보안 입문과 공격 스킬 기초 | 난이도 ★★★☆☆ | 예상 소요 시간 2~3시간

전제: Step 23~24(권한과 소유권), Step 106(setuid), Step 108(PATH 인젝션)을 마쳤다. 리눅스 터미널(WSL 포함)이 준비되어 있다.

  • 준비물: 리눅스 터미널 하나. 오늘의 모든 실험은 /tmp 안의 연습 폴더에서만 일어나며, 시스템 파일은 읽기만 합니다.
  • 주의: ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다. 오늘의 실험 자체는 100% 안전하지만, 마지막에 배우는 심볼릭 링크 악용 기법은 실제 권한 상승 공격의 재료입니다 — 경계를 기억하세요. 이 챕터의 모든 실측은 WSL 리눅스(Ubuntu 24.04)의 /tmp/linklab에서 수행했습니다.

ls -l을 하면 파일 목록이 나옵니다. 그런데 그 "파일 이름"은 사실 파일의 실체가 아닙니다. 실체는 따로 있고, 이름은 실체를 가리키는 표창일 뿐입니다. 그리고 표창은 하나의 실체에 여러 개 붙을 수 있습니다 — 이것이 링크(link)입니다. 오늘은 이 표창 시스템의 두 종류, 하드링크와 심볼릭 링크를 손으로 만들어 보고, 왜 이 무해해 보이는 기능이 권한 상승 공격의 단골 소재인지까지 봅니다.


1. 학습 목표

이 챕터를 끝내면 다음을 할 수 있습니다:

  • 파일의 실체(inode)와 이름이 분리되어 있다는 구조를 설명한다
  • 하드링크와 심볼릭 링크를 만들고, ls -li로 둘의 차이를 확인한다
  • 원본이 삭제될 때 두 링크에 어떤 일이 일어나는지 실험으로 보인다
  • readlink -f로 심볼릭 링크가 가리키는 실체를 추적한다
  • 심볼릭 링크가 어떻게 공격에 악용되는지 시나리오로 설명한다

2. 배경 지식 — 오늘의 도구와 개념

오늘의 도구 한눈에 보기

구분 내용
언어·환경 리눅스 터미널(WSL 또는 랩 VM), 연습 폴더 /tmp/linklab
오늘의 명령 ln 원본 하드링크(하드링크), ln -s 대상 링크(심볼릭 링크), ls -li(inode 확인), readlink -f(링크 추적), stat(파일 실체 정보), find -xtype l(깨진 링크 찾기)
필요한 개념 inode, 디렉터리 항목(이름→inode 대응표), 링크 수(link count), 심볼릭 링크 공격(symlink attack)
오늘의 산출물 두 링크의 차이를 실험으로 증명한 연습 폴더와 정리 노트

2-1. 파일의 실체 — inode

리눅스에서 파일은 두 층으로 나뉩니다. 데이터의 실체(내용, 권한, 소유자 등)가 담긴 번호표 붙은 덩어리가 inode(아이노드, index node)이고, 우리가 보는 "파일 이름"은 디렉터리 안에 적힌 대응표의 한 줄 — "이 이름은 몇 번 inode를 가리킨다" — 일 뿐입니다.

그래서 mv a.txt b.txt로 이름을 바꿔도 내용이 그대로인 것, 그리고 오늘 배울 "한 실체에 이름 여러 개 붙이기"가 가능한 것입니다. 이름은 표창이고, inode가 집입니다.

2-2. 하드링크 — 같은 집의 또 다른 현관문

하드링크(hard link)는 같은 inode를 가리키는 이름을 하나 더 만드는 것입니다. 원본과 하드링크는 대등합니다 — 어느 쪽이 "진짜"인지 구분할 수 없고, 둘 다 같은 실체의 문입니다. 한쪽을 지워도 다른 이름이 남아 있는 한 실체는 살아 있습니다(실제로는 링크 수가 0이 될 때 데이터가 해제됩니다).

제약이 두 가지 있습니다. 디렉터리에는 만들 수 없고, 다른 파일시스템(다른 디스크/파티션)을 넘나들 수 없습니다 — inode 번호는 파일시스템 안에서만 유효한 번호이기 때문입니다.

2-3. 심볼릭 링크 — 바로가기라는 이름의 파일

심볼릭 링크(symbolic link, symlink)는 "다른 경로를 가리키는 글자가 적힌 특수 파일"입니다. 윈도우의 바로가기(.lnk)와 비슷하지만, 리눅스에서는 대부분의 프로그램이 심볼릭 링크를 자동으로 따라가 원본처럼 취급합니다 — 훨씬 강력하고, 그만큼 위험합니다.

심볼릭 링크는 하드링크의 제약이 없습니다. 디렉터리도 가리킬 수 있고, 다른 파일시스템도, 심지어 존재하지 않는 경로도 가리킬 수 있습니다(그러면 "깨진 링크"가 됩니다).

2-4. 왜 보안 챕터에서 이걸 배우는가

프로그램이 "링크를 자동으로 따라간다"는 성질을 생각해 보세요. 관리자 권한으로 돌아가는 스크립트가 /tmp/report.txt에 결과를 기록한다고 합시다. 그런데 공격자가 미리 /tmp/report.txt/etc/shadow를 가리키는 심볼릭 링크로 만들어 두면? 스크립트는 자기도 모르게 /etc/shadow를 덮어씁니다. 이것이 심볼릭 링크 공격(symlink attack)의 기본 형태입니다.

/tmp처럼 누구나 쓸 수 있는 공용 디렉터리에서 일하는 프로그램, 예측 가능한 임시 파일 이름 — 이 둘이 만나면 사고가 납니다. 오늘의 마지막 실험에서 이 구조를 안전한 연습 폴더 안에서 재현해 봅니다.


3. 따라 하기

3-1. 준비 — 연습 폴더와 원본 파일

입력

mkdir -p /tmp/linklab && cd /tmp/linklab
echo "원본 내용 1행" > original.txt

읽는 법: /tmp는 누구나 쓸 수 있는 공용 공간이라 실험에 적합하고, 재부팅하면 자연스럽게 비워집니다. 오늘의 모든 실험은 이 폴더 안에서만 합니다.

3-2. 하드링크 만들기 — inode가 같다

입력

ln original.txt hard.txt
ln -s /tmp/linklab/original.txt sym.txt
ls -li

출력 (2026-09-09 실측):

total 8
63899 -rw-r--r-- 2 root root 19 Sep  9 15:07 hard.txt
63899 -rw-r--r-- 2 root root 19 Sep  9 15:07 original.txt
63900 lrwxrwxrwx 1 root root 25 Sep  9 15:07 sym.txt -> /tmp/linklab/original.txt

읽는 법: -i 옵션이 맨 앞에 inode 번호를 보여 줍니다. hard.txtoriginal.txt의 번호가 똑같이 63899 — 같은 실체에 이름이 두 개 붙은 것입니다. 권한 칸 다음의 숫자 2링크 수(이 실체를 가리키는 이름의 개수)입니다. 반면 sym.txt는 inode가 다르고(63900), 권한 맨 앞 글자가 l(링크 파일), 이름 옆에 -> 가리키는 대상이 표시됩니다.

: 세 파일이 한눈에 비교되는 이 한 장이 오늘의 핵심 사진입니다. 하드링크는 "구분 불가능한 또 다른 원본", 심볼릭 링크는 "별개의 파일인 바로가기"라는 것이 inode 번호로 증명됩니다.

3-3. 한쪽을 고치면 — 하드링크는 같은 몸

입력

echo "하드링크로 추가한 2행" >> hard.txt
cat original.txt

출력 (2026-09-09 실측):

원본 내용 1행
하드링크로 추가한 2행

읽는 법: hard.txt에만 썼는데 original.txt에도 줄이 추가되어 있습니다. 이름이 둘이어도 몸이 하나이니 당연한 결과입니다. "사본(copy)이 아니라 별명(alias)"이라는 감각을 몸에 붙이세요.

3-4. 심볼릭 링크 따라가기 — 무엇이든 가리킨다

이번에는 시스템 파일을 가리키는 링크를 만들어 봅니다. 읽기만 합니다.

입력

ln -s /etc/passwd passwd_link.txt
head -3 passwd_link.txt
ls -l passwd_link.txt
readlink -f sym.txt

출력 (2026-09-09 실측):

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
lrwxrwxrwx 1 root root 11 Sep  9 15:07 passwd_link.txt -> /etc/passwd
/tmp/linklab/original.txt

읽는 법: passwd_link.txt를 읽으니 /etc/passwd의 내용이 나옵니다 — 프로그램들이 링크를 자동으로 따라갔기 때문입니다. readlink -f는 링크가 꼬리를 물고 이어져도 최종 실체의 절대경로를 알려 줍니다. 로그나 스크립트에서 "이 파일의 진짜 정체가 뭐지?"를 확인하는 표준 도구입니다.

3-5. 원본 삭제 실험 — 두 링크의 결정적 차이

입력

rm original.txt
ls -li
cat hard.txt
cat sym.txt

출력 (2026-09-09 실측):

=== 원본 삭제 후 ===
total 4
63899 -rw-r--r-- 1 root root 50 Sep  9 15:07 hard.txt
63886 lrwxrwxrwx 1 root root 11 Sep  9 15:07 passwd_link.txt -> /etc/passwd
63900 lrwxrwxrwx 1 root root 25 Sep  9 15:07 sym.txt -> /tmp/linklab/original.txt
원본 내용 1행
하드링크로 추가한 2행
cat: sym.txt: No such file or directory

읽는 법: 원본을 지웠는데 hard.txt는 멀쩡히 내용을 보여 줍니다 — 링크 수가 2에서 1로 줄었을 뿐 실체는 살아 있습니다. 반면 sym.txt는 파일은 남아 있는데 따라갈 대상이 사라져 깨진 링크가 됐습니다. 에러 메시지가 재밌습니다 — sym.txt는 분명 존재하는데 No such file or directory라니요. 이 에러는 "링크 파일이 없다"가 아니라 "링크가 가리키는 대상이 없다"는 뜻입니다. 처음엔 헷갈립니다. 정상입니다.

3-6. 하드링크의 제한 — 두 가지 에러

입력

ln /tmp dir_hardlink
ln hard.txt /mnt/c/hard_cross.txt

출력 (2026-09-09 실측):

ln: /tmp: hard link not allowed for directory
ln: failed to create hard link '/mnt/c/hard_cross.txt' => '/tmp/linklab/hard.txt': Invalid cross-device link

읽는 법: 디렉터리에는 하드링크를 만들 수 없고(hard link not allowed for directory), 다른 파일시스템으로도 넘어갈 수 없습니다(Invalid cross-device link/mnt/c는 WSL에서 윈도우 드라이브라 별개의 파일시스템입니다). 둘 다 심볼릭 링크(ln -s)로는 가능합니다. 이 제약 차이가 "왜 위험한 쪽은 항상 심볼릭 링크인가"의 답이기도 합니다.

3-7. 깨진 링크 찾기

입력

find /tmp/linklab -xtype l

출력 (2026-09-09 실측):

/tmp/linklab/sym.txt

읽는 법: -xtype l은 "따라가 봤을 때 링크인 것", 즉 대상이 없는 깨진 심볼릭 링크만 골라 줍니다. 시스템 점검이나 정리 스크립트를 짤 때 유용하고, 공격자 입장에서는 "예전에 누가 흘려둔 링크"를 찾는 정찰 기법이기도 합니다.

3-8. 심볼릭 링크 공격 시연 — 연습 폴더 안에서

관리자가 돌리는 스크립트가 있다고 가정해 봅시다. 이 스크립트는 매번 /tmp/linklab/report.txt에 결과를 기록합니다. 공격자가 그 사실을 알고 미리 손을 써 두면 어떻게 되는지, 전부 내가 소유한 파일들로만 재현해 봅니다.

입력

echo "관리자만 쓸 수 있는 중요 파일" > important.txt
chmod 644 important.txt
ln -s /tmp/linklab/important.txt report.txt
echo "공격자가 심링크로 보낸 내용" > report.txt
cat important.txt
ls -l report.txt

출력 (2026-09-09 실측):

--- important.txt 결과 ---
공격자가 심링크로 보낸 내용
lrwxrwxrwx 1 root root 26 Sep  9 15:08 report.txt -> /tmp/linklab/important.txt

읽는 법: 스크립트(여기서는 내가 대신 실행)는 report.txt에 썼을 뿐인데, 정작 내용이 바뀐 것은 important.txt입니다. 쓰기가 링크를 타고 흘러가 엉뚱한 파일을 덮어쓴 것입니다. 여기서는 피해자도 가해자도 전부 내 파일이라 안전했지만, 이 구조에서 스크립트가 root로 돌고 링크의 대상이 /etc/shadow나 root의 SSH 공개키 파일이라면 — 그것이 실제 권한 상승 사고의 모습입니다.

: 방어의 출발점도 여기 있습니다. 관리자 권한 프로그램이 공용 디렉터리의 예측 가능한 이름에 쓰기를 하지 않는 것, 쓰기 전에 대상이 심볼릭 링크인지 확인하는 것(O_NOFOLLOW 같은 안전 장치)이 표준 방어법입니다.


4. 미션과 연습문제

미션 — 링크 실험 보고서

/tmp/linklab 폴더에서 다음을 수행하고, 결과를 노트(메모장, 마크다운 무엇이든)로 정리하세요:

  1. 파일 a.txt를 만들고 하드링크 b.txt와 심볼릭 링크 c.txt를 만든다.
  2. ls -li 출력을 붙여 넣고, 세 파일 중 inode가 같은 것과 다른 것을 표시한다.
  3. a.txt를 삭제한 뒤 b.txtc.txt 각각을 cat해 보고, 어떤 결과(내용 출력 / 에러 메시지)가 나오는지 그대로 기록한다.
  4. readlink -f c.txt의 출력을 기록하고, 이 명령이 무슨 일을 하는지 한 줄로 적는다.
  5. 마지막으로 "심볼릭 링크가 위험한 이유"를 3-8의 시연을 근거로 두 문장으로 정리한다.

스스로 검증하는 법: 노트에 No such file or directory 에러가 "링크 파일이 없어서"가 아니라 "대상이 없어서"라는 설명으로 기록되어 있으면 핵심을 잡은 것입니다.

연습문제

문제 1. ls -li 출력에서 하드링크와 원본이 "같은 실체"라는 것을 보여 주는 두 가지 근거는 무엇인가요?

문제 2. 심볼릭 링크를 cat했을 때 No such file or directory가 나왔습니다. 그런데 ls -l에는 그 파일이 분명히 존재합니다. 이 모순처럼 보이는 상황을 설명해 보세요.

문제 3. 하드링크는 디렉터리에 만들 수 없고 다른 파일시스템을 넘지 못합니다. 심볼릭 링크는 둘 다 가능합니다. 공격자 입장에서 어느 쪽이 더 "유용"하며, 그 이유를 제약의 관점에서 설명해 보세요.

문제 4. root 권한으로 매시간 실행되는 스크립트가 /tmp/backup.log에 로그를 덧붙인다고 합시다. /tmp에 쓰기 권한이 있는 일반 사용자가 이것을 권한 상승에 이용하려면 어떤 준비를 하면 되며, 방어자는 어떻게 막을 수 있을까요?


5. 모범 답안과 완료 기준

미션 모범 답안

수행 과정 예 (2026-09-09 실측 환경 기준):

cd /tmp/linklab
echo "미션 원본" > a.txt
ln a.txt b.txt
ln -s /tmp/linklab/a.txt c.txt
ls -li
64123 -rw-r--r-- 2 root root 7 Sep  9 15:20 a.txt
64123 -rw-r--r-- 2 root root 7 Sep  9 15:20 b.txt
64124 lrwxrwxrwx 1 root root 17 Sep  9 15:20 c.txt -> /tmp/linklab/a.txt

(출력 예시 — 여러분의 inode 번호와 시각은 다릅니다. 같은 구조이면 됩니다.)

a.txtb.txt의 inode 번호와 링크 수(2)가 같고, c.txt만 다른 inode에 -> 표시가 있음을 표시합니다. 이어서:

rm a.txt
cat b.txt   # → "미션 원본" 출력됨 (하드링크 생존)
cat c.txt   # → cat: c.txt: No such file or directory (깨진 링크)
readlink -f c.txt   # → /tmp/linklab/a.txt (없는 대상의 경로도 보여 줌)

정리 문구 예: "심볼릭 링크는 프로그램이 자동으로 따라가는 바로가기다. 관리자 권한 프로그램이 내가 쓸 수 있는 경로의 파일을 열 때, 그 파일이 링크라면 쓰기·읽기가 링크의 대상으로 흘러간다 — 3-8에서 report.txt에 쓴 내용이 important.txt를 덮어쓴 것이 증거다."

연습문제 해답

문제 1 해답. ① 맨 앞 열의 inode 번호가 동일하고(실측에서 둘 다 63899), ② 권한 칸 다음의 링크 수가 2입니다. 참고로 파일 크기·수정 시각까지 완전히 같습니다 — 같은 실체의 정보를 두 번 보여 주는 것이니까요.

문제 2 해답. 존재하는 것은 "링크 파일" 자체이고, 없는 것은 "링크가 가리키는 대상"입니다. cat은 링크를 따라가 대상을 열려 하다가 대상이 없어서 실패합니다. 에러 메시지가 링크 이름을 가리켜서 헷갈리지만, 실제로는 대상 부재를 알리는 것입니다. find -xtype l로 이런 깨진 링크만 골라낼 수 있습니다.

문제 3 해답. 심볼릭 링크 쪽입니다. 하드링크는 같은 파일시스템의 "존재하는" inode로만 연결되지만, 심볼릭 링크는 경로 문자열만으로 아무것이나 가리킵니다 — 다른 파일시스템, 디렉터리, 아직 존재하지 않는 파일, 그리고 /etc/shadow 같은 시스템 파일까지. 공격자가 목표 파일의 inode를 몰라도 경로만 알면 함정을 깔 수 있다는 점이 결정적입니다.

문제 4 해답. 공격 준비: 스크립트가 실행되기 전에 /tmp/backup.log를 삭제하고, 목표 파일(예: /etc/shadow 또는 root의 /root/.ssh/authorized_keys)을 가리키는 심볼릭 링크를 그 이름으로 만들어 둡니다. 스크립트가 로그를 쓰면 내용이 목표 파일에 기록됩니다. 방어: root 프로그램은 공용 디렉터리의 예측 가능한 파일명에 쓰지 않고, 쓴다면 링크 여부를 확인(O_NOFOLLOW)하며, /tmp의 sticky bit(Step 106)와 systemd의 PrivateTmp 같은 격리 장치를 씁니다.

완료 기준 체크리스트

  • [ ] inode가 "파일의 실체"이고 이름은 표창임을 설명할 수 있다
  • [ ] ln으로 하드링크, ln -s로 심볼릭 링크를 만들었다
  • [ ] ls -li에서 inode 번호·링크 수·-> 표시를 읽을 수 있다
  • [ ] 원본 삭제 시 하드링크는 살고 심볼릭 링크는 깨지는 것을 실험으로 확인했다
  • [ ] cat: 심링크: No such file or directory 에러의 진짜 뜻을 안다
  • [ ] readlink -f로 링크의 최종 대상을 추적할 수 있다
  • [ ] 심볼릭 링크 공격의 구조(공용 디렉터리 + 예측 가능한 이름 + 고권한 프로그램)를 설명할 수 있다
  • [ ] 미션: 링크 실험 보고서를 작성했다

6. 흔한 실수와 해결

벽 1. ln의 인수 순서가 헷갈려요

증상: ln -s sym.txt /etc/passwd처럼 거꾸로 써서 엉뚱한 링크가 생깁니다.
원인: lncp와 같은 순서입니다 — ln [-s] 원본(대상) 새이름(링크)입니다.
해결: "복사하듯이" 외우세요. cp 원본 사본이면 ln -s 대상 링크입니다. 만들고 나서 ls -l-> 방향이 의도대로인지 반드시 확인하세요.

벽 2. 링크를 지웠는데 원본이 사라질까 봐 겁나요

증상: rm sym.txt를 치기가 두렵습니다.
원인: 링크 삭제가 원본까지 지우는지 불확실하기 때문입니다.
해결: 안심하세요. rm이름(표창)만 지웁니다. 심볼릭 링크를 지워도 대상은 무사하고, 하드링크 하나를 지워도 다른 이름이 남아 있으면 실체는 살아 있습니다(3-5 실측이 그 증거입니다). 실체가 사라지는 것은 마지막 이름이 지워질 때뿐입니다.

벽 3. 하드링크를 만들었더니 에러가 나요

증상 (2026-09-09 실측):

ln: failed to create hard link '/mnt/c/hard_cross.txt' => '/tmp/linklab/hard.txt': Invalid cross-device link

원인: 하드링크는 같은 파일시스템 안에서만 가능합니다. WSL의 /mnt/c(윈도우 드라이브)는 별개의 파일시스템입니다.
해결: 파일시스템을 넘어야 한다면 심볼릭 링크(ln -s)를 쓰세요. 디렉터리에 대한 링크도 마찬가지입니다(hard link not allowed for directory).

벽 4. 상대경로로 만든 심볼릭 링크가 깨져요

증상: cd /tmp/linklab && ln -s a.txt c.txt로 만들고 다른 디렉터리에서 보니 깨진 링크입니다.
원인: 심볼릭 링크 안의 경로는 링크 파일이 있는 위치 기준으로 해석됩니다. 링크를 옮기거나 다른 곳에서 보면 상대경로가 엉뚱한 곳을 가리킵니다.
해결: 오늘 실습처럼 ln -s /tmp/linklab/a.txt c.txt처럼 절대경로로 만드는 습관이 안전합니다. 깨졌는지는 find 경로 -xtype l로 점검하세요.

벽 5. "심링크 공격이 그렇게 쉽게 되나요?"라는 의문

증상: 3-8이 너무 간단해서 실제로 먹힐지 의심스럽습니다.
원인: 좋은 의심입니다. 요즘 리눅스는 /tmp의 sticky bit, 커널의 protected_symlinks 설정 등으로 기본 방어를 갖추고 있습니다.
해결: 정직한 답 — 기본 방어가 있어도, 설정 미스·구형 시스템·자체 제작 스크립트에서는 여전히 실제로 먹히는 기법입니다. 워게임과 권한 상승 실전(Step 110에서 정리할 "허점" 목록)에서 계속 만나게 될 것입니다. 원리를 아는 것이 발견의 전제입니다.


7. 정리

오늘의 개념

개념 한 줄 설명
inode 파일의 실체(내용·권한·소유자)가 담긴 번호 붙은 덩어리
하드링크 같은 inode를 가리키는 또 다른 이름 — 원본과 대등
심볼릭 링크 다른 경로를 가리키는 특수 파일 — 바로가기
링크 수 한 inode를 가리키는 이름의 개수 — 0이 되면 해제
깨진 링크 대상이 사라진 심볼릭 링크 (find -xtype l로 탐색)
심볼릭 링크 공격 고권한 프로그램의 쓰기/읽기를 링크로 엉뚱한 파일에 흘리는 기법

오늘의 명령어

명령 하는 일
ln 원본 새이름 하드링크 생성 (같은 파일시스템, 파일만)
ln -s 대상 링크이름 심볼릭 링크 생성 (무엇이든 가리킴, 절대경로 권장)
ls -li inode 번호·링크 수·-> 대상 확인
readlink -f 링크 링크의 최종 실체 절대경로 추적
stat 파일 inode·링크 수 등 실체 정보 상세 조회
find 경로 -xtype l 깨진 심볼릭 링크 찾기

명령어보다 중요한 감각

오늘 실험이 남겨야 할 그림은 하나입니다 — 이름과 실체의 분리. 파일 시스템은 표창 관리 시스템이고, 프로그램은 표창이 아니라 그 뒤의 실체를 엽니다. 공격자는 이 간극에서 일합니다. "이 프로그램이 여는 파일은 정말 그 파일인가?"라는 질문을 던질 수 있게 된 것, 그것이 오늘의 수확입니다.

권한 모델은 겉으로 튼튼해 보여도, 이런 "표창 조작" 하나로 우회당합니다. 여러분이 지금까지 쌓아 온 setuid, PATH, 그리고 오늘의 링크 지식은 모두 같은 질문 — "설정이 의도한 대로만 동작하는가?" — 의 서로 다른 얼굴입니다.


전부 체크되면 Step 109 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.