Step 283. ★ CTF #3: 팀 첫 출전 + 목표 설정 — 측정되는 목표만이 팀을 키운다

Step 283. ★ CTF #3: 팀 첫 출전 + 목표 설정 — 측정되는 목표만이 팀을 키운다

Level 3 — CTF 실전과 공격 스킬 심화 | 난이도 ★★★☆☆ | 예상 소요 시간 1개 주말 (사전 미팅 1시간 + 대회 24~48시간 + 채점 회고 1시간)

전제: Step 279~283의 사이클 — 솔로 대회 로그(ctf_log.csv)와 팀 대회 로그(team_log.csv) 두 벌, Step 281의 팀 회고 개선 규칙, Step 282의 Write-up 루틴.

  • 준비물: 지난 두 대회의 로그 CSV, 팀 회고의 개선 규칙 문장, 다음 주말 대회, 그리고 오늘 만드는 목표 산출 스크립트.
  • ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다. CTFtime에 등록된 CTF 대회는 주최 측이 참가하라고 연 합법 플랫폼입니다 — 대회가 제공하는 문제 서버 외에는 어떤 대상도 공격하지 않습니다.
  • 주의: 대회 플랫폼 화면은 화면 예시입니다. 목표 산출 스크립트의 출력은 여러분의 로그를 넣으면 그대로 재현되는 로컬 실측입니다.

세 번째 대회부터는 게임의 규칙이 바뀝니다. 첫 대회는 완주가 목표였고, 두 번째는 협업의 체험이었습니다. 이제 두 번의 데이터가 쌓였으니, 세 번째는 목표를 세우고 채점하는 대회입니다. 목표가 있는 팀과 없는 팀의 차이는 그 대회의 성적이 아니라, 대회가 끝난 뒤 무슨 대화를 하느냐에서 나옵니다.


1. 학습 목표

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

  • 순위 목표와 행동 목표를 구분하고, 행동 목표만으로 대회 목표를 설계한다
  • 지난 대회 로그에서 풀이 수·커버리지·평균 해결 시간을 산출해 목표의 근거로 쓴다
  • 사전 미팅에서 정량 목표 3개를 합의하고 상태표에 달성 현황을 함께 표시한다
  • 대회 마지막 2시간을 "가장 가까운 문제" 집중으로 운영한다
  • 종료 후 목표 달성도를 채점하고 미달 원인을 규칙 개선으로 연결한다

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

오늘의 도구 한눈에 보기

구분 내용
언어·환경 대회 로그 CSV 2벌, 공유 상태표, 파이썬(목표 산출 스크립트)
오늘의 도구 goal_calc.py(로그 비교 → 행동 목표 초안), 목표 채점표
필요한 개념 행동 목표 vs 결과 목표, 전회 대비 +α, 분야 커버리지, 마지막 2시간 운영, 채점 회고
오늘의 산출물 행동 목표 3개 합의 문서 + 종료 후 채점 결과

2-1. 좋은 목표는 순위가 아니라 행동이다

"이번 대회 50등 안에 들자"는 나쁜 목표입니다. 순위는 우리가 통제할 수 없는 변수(다른 팀의 실력, 문제와 우리 분야의 궁합)의 함수라서, 달성해도 왜 됐는지, 실패해도 무엇을 고칠지 알 수 없습니다.

좋은 목표는 행동의 문장입니다 — 우리가 통제할 수 있고, 끝나면 참/거짓으로 채점됩니다.

나쁜 목표 (결과·통제 불가) 좋은 목표 (행동·통제 가능)
50등 안에 들기 푼 문제 수 전회 대비 +2
점수 두 배 주력 분야 착수 문제 전부 해결
강팀처럼 보이기 Write-up 푼 문제 전수 작성
많이 풀기 1시간 상태표 갱신 100% 준수

오른쪽 열의 공통점: 전부 대회 전에 "무엇을 할 것인가"로 쓰였고, 대회 후에 로그만으로 채점됩니다. 순위는 이 행동들이 쌓인 뒤에 따라오는 부산물입니다.

2-2. 목표의 크기 — 전회 대비 +α

목표가 너무 크면 무너집니다. "지난번 3개 풀었으니 이번엔 10개"는 동기가 아니라 좌절 예약입니다. 행동 목표의 적정 크기는 전회 대비 +α — 늘릴 만큼만 늘리는 점진적 목표입니다.

근거는 데이터에 있습니다. 여러분의 두 대회 로그가 말해 주는 것:

대회 #1 (솔로):  6착수 / 3해결 / 250점  — 1인 커버리지의 한계
대회 #2 (팀):    8착수 / 7해결 / 1300점 — 분업 효과, 그러나 중복 착수 1건

팀 대회의 7해결에는 분업 도입의 일회성 점프가 섞여 있습니다. 그러니 세 번째 대회의 풀이 수 목표는 "7에서 +2" 정도가 정직합니다. 점진적 목표는 보수적인 게 아니라 달성→강화의 루프를 유지하는 장치입니다 — 목표를 달성한 팀은 다음 목표를 스스로 올리고, 무너진 팀은 목표 자체를 포기합니다.

2-3. 목표의 세 종류 — 점수, 커버리지, 습관

세 번째 대회의 목표판은 세 층으로 구성하면 균형이 맞습니다.

  1. 점수 목표 — 풀이 수 또는 총점 (전회 대비 +2문제 등)
  2. 커버리지 목표 — 분야의 넓이 ("지난번 0점이던 Rev에서 1문제 해결", "미착수였던 Misc에 1문제 착수")
  3. 습관 목표 — 운영 규칙의 준수 ("상태표 1시간 갱신 100%", "Write-up 전수 작성", "스왑 구함 의무 실행")

습관 목표가 빠진 목표판은 대회 운영이 흔들릴 때 아무것도 지켜 주지 못합니다. 반대로 습관 목표는 점수와 무관하게 채점할 수 있어서, 대회가 어려웠던 날에도 팀이 가져갈 성과를 보장합니다. Step 281 회고의 개선 규칙들이 그대로 여기의 후보입니다.

2-4. 마지막 2시간 — 대회는 끝나는 법도 전략이다

48시간 대회의 마지막 2시간은 특별한 시간대입니다. 새 문제를 여는 것은 거의 항상 손해이고(환경 세팅만 하다 끝남), 막힌 문제를 무작정 계속 파는 것도 손해입니다(이틀간 안 뚫린 벽이 2시간에 뚫릴 확률).

정답은 "가장 가까운 문제"에 화력을 집중하는 것입니다. 가까움의 판별 기준은 로그에 있습니다 — stuck 메모가 "구체적인 다음 후보가 있는" 문제. 후보가 두세 개라도 있으면 2시간은 충분한 시간입니다. 반대로 "뭐가 문젠지 모르겠음" 상태의 문제는 마지막 2시간의 대상이 아닙니다 — 그것은 Step 284 복기의 대상입니다.

2-5. 채점 회고 — 목표가 있어야 미달이 정보가 된다

목표 없는 대회의 회고는 "아쉬웠다"로 끝납니다. 목표 있는 대회의 회고는 미달 항목마다 원인 분석이 붙습니다 — 미달은 실패가 아니라 측정값이기 때문입니다. "커버리지 목표 미달: Rev 문제를 착수조차 못 함 → 킥오프에서 Rev 담당이 공백이었음 → 다음 대회 전 Rev 쉬운 문제 5개 예습"처럼, 미달의 사슬 끝에는 항상 다음 행동이 있습니다.


3. 따라 하기

3-1. 지난 대회 데이터로 목표 산출하기 (실측)

먼저 숫자를 뽑습니다. goal_calc.py는 여러 대회의 로그 CSV를 나란히 읽어 비교표와 행동 목표 초안을 만들어 줍니다.

# goal_calc.py — 지난 대회 로그들을 읽어 다음 대회의 '행동 목표' 초안을 산출한다
# 사용: python goal_calc.py 대회1로그.csv 대회2로그.csv ...
import csv, sys
from datetime import datetime

FMT = "%m-%d %H:%M"
FIELDS_ALL = ["Web", "Pwn", "Rev", "Crypto", "Forensics", "Misc"]

def analyze(path):
    rows = list(csv.DictReader(open(path, newline="", encoding="utf-8")))
    started, solved, elapsed = {}, {}, {}
    for r in sorted(rows, key=lambda x: x["시각"]):
        p, t = r["문제"], datetime.strptime(r["시각"], FMT)
        if r["이벤트"] == "start":
            started[p] = (r["분야"], t, int(r["배점"] or 0))
        elif r["이벤트"] == "solve":
            solved[p] = True
            if p in started:
                elapsed[p] = int((t - started[p][1]).total_seconds() // 60)
    fields_tried = {started[p][0] for p in started}
    fields_solved = {started[p][0] for p in solved if p in started}
    pts = sum(started[p][2] for p in solved if p in started)
    avg = sum(elapsed.values()) // len(elapsed) if elapsed else 0
    return {"착수": len(started), "해결": len(solved), "점수": pts,
            "분야_시도": fields_tried, "분야_해결": fields_solved, "평균분": avg}

(이하 비교표 출력과 목표 초안 출력 부분 — 아래 실행 결과와 함께 봅니다)

솔로 대회 로그(ctf_log.csv)와 팀 대회 합산 로그(team_log.csv, Step 281의 산출물)를 넣어 실행한 결과, 실측 출력입니다.

$ python goal_calc.py ctf_log.csv team_log.csv
                   ctf_log.csv    team_log.csv
착수 문제 수                    6               8
해결 문제 수                    3               7
총점                       250            1300
평균 해결 시간(분)               28             145
분야 커버리지                  3/4             4/5

=== 다음 대회 행동 목표 초안 ===
① 풀이 수: 직전 7개 → 목표 9개 이상 (전회 대비 +2)
② 커버리지: 시도했지만 못 푼 Rev → 이번에 1문제 이상 해결
   미착수 분야 Misc → 쉬운 문제 1개라도 착수해 커버리지 넓히기
③ Write-up: 이번에 푼 문제 전수 작성 (목표 9편 기준)

읽는 법: ① 비교표에서 "평균 해결 시간 28분 → 145분"의 점프가 보입니다 — 팀 대회에서 더 어려운 문제에 도전했다는 뜻이고, 긍정적 신호입니다. ② 커버리지 4/5 — 시도조차 안 한 분야(Misc)와 시도했지만 0점인 분야(Rev)가 구분되어 보입니다. 둘은 처방이 다릅니다: 미착수는 배정 문제, 미해결은 실력 문제. ③ 초안은 그대로 확정이 아니라 사전 미팅의 안건입니다.

3-2. 사전 미팅 — 목표 3개 합의 (화면 예시)

스크립트의 초안을 들고 사전 미팅(대회 전날, 1시간)에 들어갑니다. 초안을 그대로 수용하는 게 아니라 팀의 사정으로 조정합니다.

[사전 미팅 합의 문서 — 예시]
대회: Weekend CTF 3 (Jeopardy, 토 21:00 ~ 월 21:00)

■ 행동 목표 3개 (종료 후 채점)
  1. 풀이 수 9개 이상 (전회 7개 대비 +2)
     — 근거: team_log.csv 해결 7, 스왑 규칙 정착 시 +2 현실적
  2. Rev 1문제 이상 해결 + Misc 1문제 이상 착수
     — 근거: 전회 커버리지 4/5, Rev 0점 / Misc 미착수
  3. 상태표 1시간 갱신 100% + 착수 기록 선행(기록 먼저, 문제는 나중)
     — 근거: 전회 file-upload-rce 중복 착수 40분 손실

■ 역할 점검 (전회 회고 개선점 반영)
  - 스왑 구함 의무화: 2시간 막히면 무조건 선언 (전회 packed-binary 사건)
  - Rev 담당 공백 해소: bob이 대회 전 주중에 Rev 쉬운 문제 3개 예습

각 목표에 "— 근거:" 가 붙어 있는 것을 보세요. 근거 없는 목표는 선언이고, 근거 있는 목표는 계획입니다. 그리고 전부 대회 후에 로그로 채점 가능한 문장입니다.

3-3. 대회 중 — 목표를 상태표에 싣기

목표는 미팅 문서에만 있으면 사라집니다. 상태표 맨 위에 목표 행을 추가해, 1시간 갱신 때마다 달성 현황이 함께 보이게 합니다 (화면 예시):

[상태표 상단 — 목표 현황 행]
목표1 풀이수 9개:  현재 5개 (토 24:00 기준)  → 일요일 오전까지 +2 필요
목표2 Rev 해결:    packed-2 착수 중 (bob, 40분째)
목표3 갱신 100%:   최근 갱신 23:00 ✅  (누락 0회)

이 표시의 효과는 실시간 의사결정입니다. 일요일 오후에 목표1이 위험하면 새 문제 개척을 멈추고 "가장 가까운 문제"로 전환하고, 목표2가 위험하면 Misc 착수를 미뤄 Rev에 사람을 붙입니다 — 목표가 보여야 전환이 일어납니다.

3-4. 마지막 2시간 운영

종료 2시간 전, 캡틴이 상태표를 보고 선언합니다 (화면 예시):

[종료 2시간 전 — 캡틴 선언 예시]
현재 해결 8개. 목표까지 1개.
가장 가까운 문제 후보:
  ① Web - cache-poison  (alice) — 마지막 stuck 메모: "캐시 키 우회 아이디어 있음"
  ② Crypto - lcg-basic  (minho) — 후보 공격 2개 남음
결정: 전원 ①②에 집중. 신규 착수 금지. Misc 착수 목표는 이미 달성했으니 무리하지 않음.

신규 착수 금지가 이 시간대의 유일한 절대 규칙입니다. 그리고 결과가 어떻든 — ①이 풀리든 안 풀리든 — 이 선언 자체가 목표 기반 운영의 실습입니다.

3-5. 종료 후 — 채점과 원인 분석

종료 후 회의는 채점표로 시작합니다.

[목표 채점 — 예시]
1. 풀이 수 9개     → 결과 8개  ❌ 미달
   원인: 일요일 오전 3시간이 "막힌 문제 재시도"에 소모 — 신규 착수 없이 정체
   개선: 정체 2시간 시점에 "아예 안 본 분야의 쉬운 문제"로 이동하는 규칙 추가
2. Rev 1문제 해결  → packed-2 해결 ✅ 달성
   원인 분석(긍정): bob의 주중 예습이 결정적 — 예습 루틴을 대회 전 필수로 격상
3. 갱신 100%      → 누락 1회 (토 새벽) ❌ 미달
   원인: 전원 몰입으로 알림 무시 → 캡틴이 아니라 봇이 울리는 갱신 알림 도입

미달 두 개, 달성 하나 — 이것은 실패가 아닙니다. 원래 과제의 완료 기준이 "목표 3개 중 2개 달성"인 이유를 여기서 조정해 말하자면: 첫 목표 운영에서 전부 달성하면 목표가 너무 낮았던 것이고, 전부 미달이면 너무 높았던 것입니다. 3개 중 1~2개 달성이 목표 크기가 적정했다는 증거이고, 미달 항목의 원인 분석이 다음 대회의 규칙이 됩니다.


4. 미션과 연습문제

미션 — 목표 기반 대회 운영

  1. goal_calc.py에 지난 두 대회 로그를 넣어 비교표와 목표 초안을 산출합니다.
  2. 사전 미팅에서 행동 목표 3개를 합의합니다 — 각 목표에 "— 근거:" 한 줄을 붙이고, 전회 회고 개선점의 반영 여부를 점검합니다.
  3. 대회 중 상태표에 목표 현황 행을 유지하고, 종료 2시간 전에 "가장 가까운 문제" 집중을 선언합니다.
  4. 종료 후 채점표를 작성하고, 미달 항목마다 원인 → 개선 규칙의 사슬을 완성합니다.
  5. 목표 3개 중 2개 이상 달성을 노립니다 — 미달 항목이 있어도 원인 분석이 완성됐다면 이 미션은 성공입니다.

연습문제

문제 1. "50등 안에 들기"가 나쁜 목표인 이유를 통제 가능성과 채점 가능성의 관점에서 설명해 보세요.

문제 2. 3-1의 실측 비교표에서 "평균 해결 시간 28분 → 145분"의 증가를 어떻게 해석해야 하나요? 이것이 나쁜 신호가 아닌 이유를 말해 보세요.

문제 3. 커버리지 목표에서 "미착수 분야"와 "시도했지만 0점인 분야"의 처방이 왜 다른지 설명해 보세요.

문제 4. 마지막 2시간에 "신규 착수 금지"가 절대 규칙인 이유를 기대값 관점에서 설명해 보세요.


5. 모범 답안과 완료 기준

미션 모범 답안

검증하는 법: ① 사전 미팅 문서의 목표 3개가 전부 행동 문장이고 "— 근거:"가 로그의 숫자를 가리키는가. ② 순위·점수 등 통제 불가 목표가 섞여 있지 않은가. ③ 상태표에 목표 현황이 시간대별로 갱신된 흔적이 있는가. ④ 종료 2시간 전 선언(또는 그에 준하는 집중 결정)의 기록이 있는가. ⑤ 채점표의 미달 항목마다 원인과 개선 규칙이 짝으로 있는가 — 미달만 적고 개선이 없으면 복기가 아니라 점수판입니다.

연습문제 해답

문제 1 해답. 순위는 다른 팀의 실력과 문제 편성이라는 통제 불가 변수의 함수라서, 최선의 운영을 하고도 미달할 수 있고 형편없는 운영으로도 달성될 수 있습니다. 채점해도 "무엇을 고칠지"가 나오지 않습니다. 행동 목표는 우리가 통제할 수 있고 로그로 참/거짓이 채점돼, 미달 시 원인 분석으로 다음 행동이 도출됩니다.

문제 2 해답. 솔로 대회는 풀 수 있는 쉬운 문제 위주로 착수했고(평균 28분), 팀 대회는 분업 덕분에 200~300점대의 어려운 문제에 도전했습니다(평균 145분). 해결 시간의 증가는 느려진 것이 아니라 도전 수준이 올라간 것입니다. 해석의 확인은 총점이 봅니다 — 250점에서 1300점으로. 시간이 늘었는데 점수가 같았다면 그때가 나쁜 신호입니다.

문제 3 해답. 미착수는 배정의 문제입니다 — 킥오프에서 담당 공백이었거나 아예 문제를 읽지 않은 것이므로 처방은 운영 개선(배정 명시, 착수 할당)입니다. 시도 후 0점은 실력의 문제입니다 — 착수는 했으나 벽에 막힌 것이므로 처방은 학습(블록 A 복기, 주중 예습)입니다. 같은 0점이라도 원인 계통이 다르므로, 스크립트가 둘을 구분해 보여 주는 것입니다.

문제 4 해답. 새 문제는 환경 세팅과 문제 이해에 고정 비용(통상 30분 이상)이 들고, 남은 90분 안에 돌파까지 이를 확률은 이틀간 데이터가 없는 문제라 예측 불가입니다. 반면 "가장 가까운 문제"는 남은 후보가 구체적이라 2시간의 기대 성공 확률이 가장 높습니다. 기대값이 알려진 선택지와 불가지의 선택지가 있을 때, 짧은 잔여 시간에서는 전자가 정답입니다.

완료 기준 체크리스트

  • [ ] 행동 목표와 결과 목표의 차이를 예를 들어 설명할 수 있다
  • [ ] 지난 두 대회 로그로 비교표와 목표 초안을 산출했다
  • [ ] 행동 목표 3개를 근거와 함께 합의 문서로 남겼다
  • [ ] 상태표에 목표 현황을 싣고 1시간 갱신과 함께 유지했다
  • [ ] 종료 2시간 전 "가장 가까운 문제" 집중 운영을 실행했다
  • [ ] 채점표의 미달 항목마다 원인 → 개선 규칙을 완성했다
  • [ ] 목표 3개 중 2개 달성을 목표로 하는 이유(적정 크기)를 설명할 수 있다

6. 흔한 실수와 해결

벽 1. 목표가 너무 커서 대회 중반에 전원이 체념했어요

증상: 토요일 밤 시점에서 목표 달성이 수학적으로 거의 불가능해지자, 상태표 갱신이 멈췄습니다.

원인: 목표를 전회 데이터 없이 기분으로 정했습니다 — "이번엔 15개" 같은.

해결: 목표는 반드시 goal_calc.py의 직전 대회 수치에서 로만 정하세요. 그리고 목표를 세운 뒤 "토요일 자정 시점에 N개면 순조"라는 중간 체크포인트를 하나 더 정해 두면, 체념 대신 조기 전환이 가능합니다.

벽 2. 목표를 세웠는데 대회 중에 아무도 안 봐요

원인: 목표가 미팅 문서에만 있고, 팀이 하루 종일 보는 상태표에 없었습니다.

해결: 목표는 상태표 맨 위 행에 상주시키세요 (3-3). 문서에 있는 목표는 대회 시작 3시간이면 잊힙니다. 보이는 곳에 있는 목표만 운영에 개입합니다.

벽 3. 목표 산출 스크립트가 에러를 뱉어요

증상 (실측 — 로그 파일을 인자 없이 실행한 경우):

$ python goal_calc.py
Traceback (most recent call last):
  File ".../goal_calc.py", line 48, in <module>
    last, prev = stats[-1], ...
IndexError: list index out of range

원인: 비교할 로그 CSV를 인자로 안 넣었습니다. 스크립트는 최소 1개(의미 있는 비교는 2개)의 로그가 필요합니다.

해결: python goal_calc.py ctf_log.csv team_log.csv처럼 로그 파일들을 인자로 넘기세요. 첫 대회만 치른 상태라면 파일 하나만 넣어도 요약은 나옵니다 — 비교는 다음 대회부터 쌓입니다.

벽 4. 미달 원인 분석이 "다음에 잘하자"로 끝나요

원인: 원인을 사람(의지)에서 찾았습니다. 의지 원인의 처방은 항상 "잘하자"가 되고, 그 문장은 다음 대회에서 사라집니다.

해결: 원인을 절차에서 찾으세요 — "새벽에 갱신을 놓쳤다(의지)"가 아니라 "갱신 알림을 사람이 아니라 봇이 울리게 안 했다(절차)". 절차 원인의 처방은 규칙 문장이 되고, 규칙 문장은 다음 킥오프의 안건이 됩니다.

벽 5. 목표를 달성했는데 성장한 느낌이 없어요

원인: 목표가 전부 점수 목표였을 수 있습니다. 점수는 지난 대회에서 이미 아는 것을 다시 한 결과일 수 있습니다.

해결: 목표판의 세 층(2-3)을 점검하세요 — 커버리지 목표와 습관 목표가 있었는지. 특히 습관 목표(상태표, 스왑, Write-up)는 점수가 정체인 시기에도 팀이 강해지고 있다는 것을 보여 주는 지표입니다. 세 층이 다 있는 대회는 성장의 체감이 다릅니다.


7. 정리

오늘의 개념

개념 한 줄 설명
행동 목표 통제 가능하고 로그로 채점되는 목표 — 순위 목표의 대안
전회 대비 +α 목표의 적정 크기 — 달성→강화 루프를 유지하는 점진성
목표의 세 층 점수 목표 + 커버리지 목표 + 습관 목표
미착수 vs 미해결 전자는 배정 문제(운영), 후자는 실력 문제(학습) — 처방이 다름
마지막 2시간 신규 착수 금지 + "가장 가까운 문제" 전원 집중
채점 회고 미달 = 실패가 아니라 측정값 — 원인→규칙의 사슬로 완성
3개 중 2개 첫 목표 운영의 적정 달성률 — 전부 달성이면 목표가 낮았던 것

오늘의 명령어와 도구

명령·도구 하는 일
python goal_calc.py 로그1.csv 로그2.csv 대회 간 비교표 + 행동 목표 초안 산출
사전 미팅 합의 문서 근거 있는 행동 목표 3개의 확정
상태표 목표 현황 행 목표의 실시간 가시화 — 전환 의사결정의 근거
목표 채점표 종료 후 달성도 채점 + 미달 원인 → 개선 규칙

핵심 감각

대회 사이클의 세 챕터를 돌아보세요 — 첫 대회는 측정했고(Step 279), 복기는 벽을 해체했고(Step 280·282), 세 번째 대회는 목표를 세우고 채점했습니다(오늘). 이제 여러분의 팀에는 데이터가 도는 루프가 있습니다: 대회 → 로그 → 복기 → 목표 → 대회. 이 루프가 도는 한, 여러분의 성장은 더 이상 운이 아니라 시스템입니다.

루프의 다음 바퀴는 팀 단위의 약점 진단입니다 — 세 대회의 데이터를 분야별로 시각화해 "우리 팀의 지도"를 그리는 일. 데이터는 이미 로그 안에 있습니다.


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