영남대학교 화학공학부 · 2026학년도 2학기
화학공학을 위한 AI
바이브코딩으로 배우는 화공 도구 제작
v3.0 · 2026-09-02
머리말
다음 상황을 생각해 보자. 화학공학과 2학년 학생이 과제로 받은 증기압 계산을 AI에게 맡긴다. 몇 초 만에 파이썬 함수가 생성되고, 실행하면 숫자가 나온다. 코드는 깔끔하고 주석도 친절하다. 남는 문제는 하나다. 그 숫자가 맞는지 학생 자신이 판정하지 못한다는 것이다.
이 책은 그 지점에서 출발한다. 코드를 한 줄씩 작성하는 비용은 지난 몇 년 사이 급격히 낮아졌다. 문법을 암기하고 타이핑하는 능력은 더 이상 공학 계산의 병목이 아니다. 병목은 다른 곳으로 옮겨 갔다. 문제를 계산 가능한 형태로 분해하는 능력, 생성된 코드를 읽고 무엇을 하는지 설명하는 능력, 그리고 결과가 물리적으로 말이 되는지 판정하는 능력이다. 앞의 것은 AI가 대신하지만, 뒤의 세 가지는 대신하지 못한다.
그렇다고 AI를 만능 교사로 믿을 근거도 아직 없다. AI 도구가 학습에 어떤 조건에서 도움이 되는지는 교육 현장에서 검증이 진행 중인 주제다. 과장된 기대와 막연한 공포 사이에서 이 책이 택한 태도는 실사용이다. 도구를 실제 화공 계산에 붙여 보고, 잘 되는 지점과 틀리는 지점을 지면에 그대로 올려 다룬다.
그래서 이 수업은 학생의 역할에 대해서 정의하고 간다. AI가 코드를 쓴다. 학생은 다음 세 가지를 책임진다. 첫째, 문제를 함수 단위로 분해하고 프롬프트를 설계한다. 둘째, 생성된 코드를 읽고 자기 말로 설명한다. 셋째, 결과를 화학공학 지식으로 검증한다. 설명하지 못하는 내용을 자신의 지식으로 착각하는 것이 본 수업에서 가장 조심해야할 부분이다.
따라서 본 수업에서는 검증의 역할을 중요시 한다. 검증을 수업의 중심에 둔 이유는 공학의 오래된 관례에 있다. 계산 결과가 틀렸을 때 책임은 도구가 아니라 그 결과에 서명한 엔지니어에게 돌아간다. 손으로 풀든, 상용 시뮬레이터를 쓰든, AI에게 시키든 이 구조는 바뀌지 않는다. 바뀐 것은 검증해야 할 결과가 자기 손이 아니라 기계에서 나온다는 점뿐이다. 그래서 이 책은 검증과정을 매 장 반복하는 세 가지 루틴으로 고정했다. 입력과 출력의 단위를 확인하는 단위 체크, NIST나 교과서의 알려진 값 한 점과 비교하는 대조값 확인, 그리고 극한 조건에서 물리적으로 말이 되는지 살피는 극한값 확인이다. 도구가 바뀌어도 위 루틴은 바뀌지 않는다는 것을 책 구성의 형식의 반복으로 몸에 새기는 것이 이 책의 중요한 설계점이다.
다루는 범위를 2학년 기이수·동시수강 과목(일반화학, 화공양론, 유체역학)으로 한정한 것도 같은 논리다. 학생이 검증할 수 없는 내용은 만들게 하지 않는다. 반응기 설계처럼 3학년에서 배우는 내용으로 도구를 만들면, 코드가 돌아가도 결과의 옳고 그름을 판정할 지식이 없어 역할 계약이 무너진다. 대신 단위환산에서 시작해 물성 조회, 증기압, 기액평형, 물질수지, 배관 압력강하, 데이터 피팅과 대시보드까지, 검증 가능한 범위 안에서 도구의 사다리를 한 칸씩 올라간다. (추가적으로, 3학년 내용이나 학생들에게 친숙하지 못한 개념은 수업 중간에 화공이론 내용을 제공할 예정이다.)
아직 화공 개념이 충분치 않은 상황에서 2학년 수업에 이 수업을 배정한 것은 의도적이다. 이 수업을 통해서 앞으로의 화공엔지니어로서 성장함에 있어 두 가지를 갖추게 하고 싶었다. 첫번째는 아홉 개의 도구가 든 도구상자다. 각 도구는 1회성 과제가 아니라 다음 도구가 불러 쓰는 부품이며, 학기말 프로젝트는 이 부품의 재조립으로 완성된다. 다른 하나는 학습 OS다. 초반 4주에 만드는 강의노트 정리기, 플래시카드 생성기, 스터디 플래너는 본인의 지식 저장소(위키)를 읽고 쓰는 도구여서, 도구를 쓸수록 위키가 자란다. 이 시스템은 수업이 끝나도 다른 전공 과목의 공부에 그대로 남는다.
이 수업을 설계하며 국내외 사례를 조사한 범위에서, AI 전제 프로그래밍 입문이나 화공 수치계산 과목 같은 부분 선례는 있었지만 프로그래밍 무경험 공학도 저학년이 전공 지식으로 AI 산출물을 검증하는 것을 축으로 삼은 정규 수업은 찾지 못했다. 그래서 이 책도 기존 교재의 개정판이 아니라 새로 설계되었다. 읽기만 하는 책이 아니다. 1장의 첫 실습에서부터 손을 움직여, 매주 도구를 하나씩 만들며 읽어 나가자.
이 책의 사용법
투트랙 수업과 장 구성
이 책의 각 장은 15주 수업의 한 주와 1:1로 대응한다. 한 주는 두 트랙으로 구성된다. 이론 세션에서는 그 장의 본문 절을 읽기 자료로 쓰고, 실습 세션에서는 장 뒷부분의 실습 가이드를 따라 그 주의 도구를 만든다. 매주 실습은 최종 실기시험과 같은 형식의 축소판이므로, 학기가 끝날 때까지 같은 시험을 열다섯 번 연습해보는 것을 의도하였다.
15주의 흐름은 크게 네개의 구간으로 나뉜다.
| 구간 | 주차 | 내용 |
|---|---|---|
| 훅 | 1~4주 | 학습 OS 조립(강의노트 정리기·플래시카드 생성기·스터디 플래너) |
| 근육 | 5~7주 | 생성 코드 읽기·검증 훈련, 첫 화공 도구, 시험1(최소한의 코딩이해를 위한 지필 시험) |
| 사다리 | 8~13주 | Antoine 증기압부터 화공대시보드까지 화공 도구를 시간 점증 실기 형식으로 하나씩, 시험2(90분 리허설) |
| 완성 | 13~15주 | 서브프로젝트 발표, 시험3(2시간 실기) |
각 장의 안에는 아래 장치들이 매 장 같은 자리에 같은 모양으로 반복된다. 장치의 생김새가 15개 장 내내 바뀌지 않으므로, 1~2장을 읽고 나면 책의 사용법 자체가 몸에 익는다.
| 장치 | 자리 | 역할 |
|---|---|---|
| 도입 | 장 머리 | 그 주의 도구가 없을 때 무엇이 귀찮고 위험한지를 보여 주는 상황 묘사 |
| 이 장에서 다루는 것 | 장 머리 | 내용 예고 3~5줄 |
| 학습목표 | 장 머리 | "~할 수 있다" 행동동사형 목표. 번호가 스스로 점검·연습문제·시험 루브릭과 대응 |
| 실습 연결 | 장 머리 | 이번 주 실습과 최종 시험의 관계 안내 |
| 프롬프트-응답 리스팅 | 본문 | 프롬프트 / 생성 코드 / 검증의 3단 고정 형식 |
| AI가 이렇게 틀린다 | 본문 | 실제 재현 가능한 오류 사례(상황, 틀린 출력, 탐지법, 교정 프롬프트) |
| 예제 | 본문 | 문제 → 풀이 전략 → 코드/계산 → 검증 → 분석의 5단 형식 |
| 검증 루틴 3종 | 예제 안 | 단위 체크 · 대조값 · 극한값 |
| AI 협업 사이클 | 본문 | 문제 정의 →분해 →프롬프트 →생성 코드 읽기 →테스트 →화공 검증의 6단계 공용 도식. (지금 훈련 중인 블록을 표시) |
| 스스로 점검 | 절 끝 | 1분 안에 답하는 개념 체크. |
| 생각해보기 | 본문 | 정답이 하나가 아닌 개방형 질문 |
| 실습 가이드 | 장 뒤 | 준비물 → 단계별 과제 → 완성 기준(통과할 테스트 명문화) → 막혔을 때 힌트 프롬프트 |
| 위키 기록 과제 | 실습 끝·장 끝 | 그 주에 만든 것·막힌 것·틀린 것의 기록 지시. 과제 점수와 직결 |
| 요약·용어 정리·연습문제 | 장 끝 | 핵심 패턴 재수록(S번호)과 비교표, 영한 용어 목록, Q/P 2군 연습문제(난이도 ●·●●·●●●) |
실습 가이드에 완성 코드는 실려 있지 않다. 막혔을 때 주어지는 것은 정답이 아니라 "AI에게 이렇게 물어라"라는 힌트 프롬프트다. 매주 실습이 시험의 리허설이라는 설계를 유의하길 바란다.
평가 체계와의 연결
이 수업의 평가는 총 100점이며, 평가마다 AI 사용 규정이 다르다.
| 평가 | 배점 | 시기 | AI | 형식 |
|---|---|---|---|---|
| 시험1 | 20 | 7주 | 금지 | 오프라인 지필 |
| 시험2 | 20 | 12주 | 필수 | 90분 현장 실기 |
| 시험3 | 40 | 15주+시험주간 | 필수 | 2시간 바이브코딩 + GitHub |
| 과제 | 10 | 상시 | 허용 | LLM 위키 6점 + 서브프로젝트 4점 |
| 출석 | 10 | 상시 | — | 실습 대면 5 + 이론 LMS 5 |
최종 시험이 AI 필수 실기인데 시험1이 AI 금지인 이유는 반대 방향의 검증이다. AI 없이 남는 개인의 능력, 곧 코드 읽기·오답 교정·의사코드 설계를 별도로 확인한다.
책의 장치는 이 배점표와 직결된다. 연습문제 Q군(개념·읽기)은 AI 없이 풀 수 있어야 하며 시험1의 문항 유형과 동형이다. P군(제작·계산)은 AI 사용을 전제하며 시험2·3의 실기와 동형이다. 매 장의 위키 기록 과제는 과제 6점(5·10·15주 마일스톤 3회)의 채점 대상이 되는 주간 제출기준 공지할 예정이다. 서브프로젝트 4점은 9주차에 주제를 제출하고, 본인이 만든 도구를 2개 이상 재사용해 완성한다.
표기법
단위
단위는 SI를 기본으로 한다. 화공 실무에서 통용되는 관용 단위(atm, mmHg, bar)는 첫 등장 시 "1 atm(= 101.325 kPa)"처럼 SI 환산값을 병기한 뒤에 쓴다. 수치와 단위 기호 사이에는 공백을 둔다(25.3 kPa, 100 °C). 단위 기호는 이탤릭 없이 정립으로 쓴다.
기호와 번호 체계
| 항목 | 규칙 | 예 |
|---|---|---|
| 변수 | 이탤릭 | T, P, xA |
| 무차원수 | 정립 2자 | 레이놀즈 수 Re |
| 첨자 | 의미 첨자는 정립, 변수 첨자는 이탤릭 | pA(성분 A), xi |
| 수식 번호 | 장 단위 일련 (N.M) | "식 (9.2)를 식 (9.1)에 대입하면 다음을 얻는다" |
| 그림·표·코드 | 그림 N-M, 표 N-M, 코드 N-M | 그림 8-2, 표 8-1, 코드 8-3 |
| 로그 | 밑 명시 | log10, ln |
로그의 밑을 항상 명시하는 이유는 이 책의 소재와 직결된다. Antoine 식의 상수는 출전에 따라 log10과 ln, bar와 mmHg가 뒤섞여 유통되며, 이 혼선이 AI가 물성 상수를 날조하거나 잘못 짝짓는 대표 지점이다. 따라서 이 책의 모든 Antoine류 상수 표에는 로그 밑, 압력 단위, 온도 단위, 유효 온도범위 네 가지를 빠짐없이 명기한다.
코드
본문 속 코드 식별자는 fsolve, numpy처럼 고정폭 서체로 쓴다. 코드 블록의 주석은 한국어로 달고, 물리량을 담는 변수에는 단위를 주석으로 명기한다(T = 373.15 # K). 본문 리스팅은 20줄을 상한으로 하며, 넘는 코드는 함수 단위로 나누어 싣는다. 에러 메시지는 요약하거나 의역하지 않고 전문 그대로 싣는다. 지면의 문자열과 화면의 문자열이 일치해야 검색과 질문이 가능하기 때문이다. 상수는 이름 있는 변수로 뽑고 출처를 주석으로 남긴다. 검증 루틴이 대조할 대상이 코드 안에 보여야 한다.
프롬프트 리스팅 규약
AI와의 대화는 이 책에서 코드 리스팅과 동급의 형식을 갖는다. 모든 프롬프트-응답 리스팅은 다음 3단으로 고정된다. 학생이 쓴 프롬프트는 자연어이므로 고정폭 서체를 쓰지 않고, 다듬지 않은 실제 입력을 그대로 싣는다. AI가 생성한 코드는 점선 테두리로 표시하는데, 이는 "그럴듯하지만 아직 검증 전"이라는 뜻의 시각 기호다. 마지막 검증 단에는 사람이 무엇을 확인했고 무엇이 틀렸는지를 적는다. 검증 단이 비어 있는 리스팅은 이 책 어디에도 없다.
섭씨 온도를 켈빈으로 바꾸는 파이썬 함수를 만들어 줘. 주석은 한국어로 달아 줘.
def celsius_to_kelvin(t_c):
# 섭씨 온도[°C]를 켈빈[K]으로 변환한다
return t_c + 273.15 # K
검증: t_c = 25를 넣으면 298.15를 반환한다. 켈빈의 정의(T[K] = T[°C] + 273.15)와 일치. 다만 t_c = -300처럼 절대영도 아래의 비물리적 입력도 그대로 통과시킨다 (이런 빈틈을 찾아내는 일이 이 책 전체에서 학생의 몫이다.)
AI 도구의 지칭은 본문 전체에서 "AI"로 통일한다. 특정 제품명과 설치·설정은 1장과 부록 A에서만 다룬다.
1장 AI 시대의 화학공학자
이번 주의 도구: 작동하는 개발환경과 나의 위키 저장소
다음 상황을 생각해 보자. 화공양론 과제 마감 전날, 메탄올–물 혼합물 문제에 필요한 증기압 값을 찾던 학생이 AI 챗봇에 물었다. 답은 3초 만에 왔다. 정돈된 표, 자신 있는 말투, 그럴듯한 숫자를 내놓는다.
같은 시각 옆자리 학생은 다른 길을 택했다. AI에게 값을 묻는 대신 증기압을 계산하는 파이썬 함수를 만들게 하고, 자신은 그 코드가 쓰는 상수를 화공양론 교과서 부록과 대조했다. 두 값이 달랐다. 첫 번째 학생이 받은 표의 숫자는 어느 문헌에도 없는 값이었다.
두 학생 모두 AI를 썼다. 갈린 것은 도구가 아니라 역할이다. 한 사람은 AI에게 답을 맡겼고, 다른 사람은 AI에게 노동을 맡기고 판단을 자신이 가졌다. 이 수업의 15주는 두 번째 학생이 되는 훈련이다.
이 장에서 다루는 것
- 이 수업에서의 본인의 역할과 "설명 못 하는 지식의 위험성"에 대해 논의한다.
- AI 협업 사이클 6단계와 프롬프트-응답 리스팅 규격을 설명한다.
- 평가 5종과 AI 활용 규칙을 안내한다.
- LLM의 작동을 최소한으로 해부한다: 다음 토큰 예측, 확률적 출력, 그럴듯한 오답.
- 개인 PC에서 개발환경을 셋업하고, 첫 바이브코딩을 하고, 위키 저장소를 개설한다.
학습목표: 이 장을 마치면 다음을 할 수 있다.
- 학습에 있어 AI와 학생 각각의 책임을 구분하여 설명할 수 있다.
- AI 협업 사이클 여섯 단계를 순서대로 나열하고, 각 단계에서 사람이 확인할 것을 말할 수 있다.
- AI 활용 규칙을 구체적 상황에 적용해 허용 여부를 판정할 수 있다.
- LLM이 다음 토큰 예측으로 작동함을 간단한 프로그램으로 설명할 수 있다.
- 같은 프롬프트에서 다른 출력이 나오는 이유를 말하고, 그 실무적 귀결을 제시할 수 있다.
- 개발환경을 설치하고 첫 바이브코딩 결과를 GitHub에 커밋할 수 있다.
이번 주 실습은 §1.2의 사이클을 개발환경 셋업 직후 15분짜리 첫 바이브코딩으로 한 바퀴 돈다. 이 형식은 최종 시험(2시간 실기)의 축소판이며 15주 동안 매주 반복된다. 셋업 절차의 전문은 부록 A에 있다.
1.1 역할 정의: 코드는 AI가 쓰고, 판정은 사람이 한다
화학공학자의 하루에는 계산이 흐른다. 단위를 환산하고, 물성을 조회하고, 수지식을 풀고, 실험 데이터를 피팅한다. 계산 하나하나는 어렵지 않지만 반복되고, 반복되는 계산은 실수를 부른다. 도구를 만들 줄 아는 공학자는 반복을 프로그램으로 바꾼다.
지금까지 도구 제작의 병목은 프로그래밍 문법이었다. 대형 언어 모델의 등장이 그 병목을 옮겼다. 자연어로 의도를 말하면 AI가 코드를 작성하고, 사람이 그 코드를 읽고 검증하는 작업 방식을 바이브코딩(vibe coding)이라 한다. 문법을 몰라도 코드가 생긴다. 그렇다면 새 병목은 무엇인가. 생긴 코드가 옳은지 판정하는 능력이다.
이 수업은 그 판정 능력을 역할 분담 형식으로 정의한다. AI가 코드를 쓰고, 학생은 ① 문제 분해 ② 생성 코드 읽기·설명 ③ 화학공학적 검증을 책임진다. 이를 이 수업의 역할 계약이라 한다. AI 코드 어시스턴트를 전제로 프로그래밍 입문 교육을 재설계한 미국 UCSD의 CS1-LLM 모델과 같은 노선이다. 코드를 짜는 기술이 아니라 코드를 부리는 기술을 배워야 한다.
이 계약에는 벌칙 조항이 있다. 자신이 제출한 코드를 설명하지 못하면 해당 이해 점수는 0점이다. 최종 시험의 구술 방어에서는 지목 질문 3개 중 2개 이상 설명에 실패하면 코드 이해 차원 전체가 0점으로 처리된다. 돌아가는 코드와 이해한 코드는 다르며, 이 수업이 점수를 주는 쪽은 후자다.
검증에 "화학공학적"이라는 수식어가 붙는 이유가 있다. 컴퓨터는 문법 오류를 잡아 주지만, 단위가 뒤집힌 계산과 날조된 물성값은 문법적으로 완벽한 코드 안에서 조용히 살아남는다. 그것을 잡는 것은 전공 지식이다. 단위가 맞는가, 알려진 값과 맞는가, 극한에서 물리적으로 말이 되는가. 이 세 질문을 검증 루틴 3종이라 하며 3장에서 정식으로 도입한다. 이 장에서는 형식을 눈에 익히는 것으로 충분하다.
스스로 점검 1.1
- 역할 계약에서 학생이 책임지는 세 가지는 무엇인가?
- "코드가 돌아간다"만으로 과제가 끝나지 않는 이유는 무엇인가?
1.2 AI 협업 사이클: 여섯 단계 한 바퀴
역할 계약을 매주 실행 가능한 절차로 서술한 것이 AI 협업 사이클이다. 이 책의 모든 예제와 실습은 이 여섯 단계 위에서 움직이며, 각 장은 지금 어느 단계를 훈련 중인지 표시한다.
- 문제 정의: 구할 것과 단위를 문장으로 확정한다
- 분해: 문제를 함수 하나 크기의 조각으로 나눈다
- 프롬프트: 조각 하나를 AI에게 요청한다
- 생성 코드 읽기: 받은 코드를 한 줄씩 이해한다
- 테스트: 실행하고 예상과 비교한다
- 화공 검증: 단위·대조값·극한값으로 물리적 타당성을 판정한다
위 사이클과 함께 이 책의 형식을 말하고 넘어가고자 한다. AI와의 대화는 코드 리스팅의 새로운 형식이므로, 세 단으로 고정해 싣는다. [프롬프트]는 사람이 쓴 요청을 다듬지 않고 그대로, [생성 코드]는 AI가 낸 코드를 점선 테두리로 (점선은 "아직 검증 전"이라는 뜻이다) , [검증]은 사람이 확인한 것과 고친 것을 담는다.
말로만 들은 사이클은 아직 손에 잡히지 않는다. 한 바퀴를 실제로 돌아 보자.
예제 1.2-1 첫 바이브코딩: 이상기체 몰 부피 계산기
문제 온도 273.15 K, 압력 100 kPa에서 이상기체 1 mol이 차지하는 부피를 계산하는 파이썬 함수를 AI와 함께 만들고, 결과를 검증하라.
풀이 전략 일반화학에서 배운 이상기체 상태방정식 PV = nRT를 1 mol 기준 몰 부피에 대해 정리하면 식 (1.1)을 얻는다.
Vm = RT/P (1.1)
사이클 ①·②는 이미 끝났다. 구할 것은 몰 부피(단위 m³/mol), 조각은 함수 하나다. 코드 작성(③)은 AI에게 맡기고, 사람은 읽기(④)·테스트(⑤)·화공 검증(⑥)을 맡는다.
이상기체의 몰 부피를 계산하는 파이썬 함수를 만들어 줘. 입력은 온도(K)와 압력(Pa), 출력은 m³/mol. 기체상수는 이름 있는 변수로 두고 출처를 주석에 적어 줘. 주석은 한국어로 작성해줘.
R = 8.314 # 기체상수 [Pa·m3/(mol·K)] — 출처: NIST CODATA
def molar_volume(T, P):
"""이상기체 몰 부피를 계산한다.
T: 절대온도 [K]
P: 압력 [Pa]
반환: 몰 부피 [m3/mol]
"""
if T <= 0:
raise ValueError("절대온도는 0 K보다 커야 한다")
return R * T / P
# 0 °C, 1 bar에서 확인 실행
V = molar_volume(273.15, 1.0e5) # [K], [Pa]
print(f"몰 부피 = {V:.5f} m3/mol = {V * 1000:.1f} L/mol")
검증: 상수 R의 값·단위·출처 주석 확인. 수식은 식 (1.1) 그대로다. 요청하지 않은 온도 방어 코드(T <= 0)가 추가됐는데, 물리적으로 타당하므로 수용한다. 실행 출력: 몰 부피 = 0.02271 m3/mol = 22.7 L/mol.
- 단위: Pa·m³/(mol·K) × K ÷ Pa = m³/mol. 입력·출력 단위가 식 (1.1)과 일치한다.
- 대조값: 273.15 K, 10⁵ Pa에서 22.7 L/mol. 화공양론 교재의 표준상태 몰 부피와 일치한다(LibreTexts Foundations 3.06 기준).
- 극한값: P를 2배로 하면 Vm은 정확히 절반(반비례 확인). T → 0에서 Vm → 0은 식으로는 옳지만 실제 기체는 그 전에 액화한다 (모델의 적용 한계까지 극한 검사가 드러낸다.)
분석 매우 간단한 예제이다. 다만 이 예제에서 중요한 점은 "코드가 맞았다"는 결과가 아니라 맞았음을 확인한 절차다. 같은 절차가 15주 내내, 그리고 마지막 시험의 검증 증빙 항목에서 반복된다. 프롬프트의 압력 단위를 Pa에서 kPa로 바꿔 재생성하고, 코드의 어디가 달라지는지·검증 세 줄이 어떻게 바뀌는지 확인해 보라.
스스로 점검 1.2
- 사이클 여섯 단계 중 AI가 대신하는 노동은 어디에 있는가?
- 예제 1.2-1의 대조값 검증에 쓴 값은 무엇이었는가?
1.3 이 수업의 AI 활용 규칙
AI 사용 규칙은 평가마다 다르다. 금지·필수·허용의 세 종류로 나뉘며, 이를 AI 3-lane 규칙이라 한다. 표 1-1이 강의에서 AI를 활용하게 될 전체 구조다.
| 평가 | 배점 | 시기 | AI | 형식 |
|---|---|---|---|---|
| 시험1 | 20 | 7주 | 금지 | 오프라인 지필 |
| 시험2 | 20 | 12주 | 필수 | 90분 현장 실기 |
| 시험3 | 40 | 15주+시험주간 | 필수 | 2시간 바이브코딩 + GitHub |
| 과제 | 10 | 상시 | 허용 | LLM 위키 6점 + 서브프로젝트 4점 |
| 출석 | 10 | 상시 | — | 실습 대면 5 + 이론 LMS 5 |
최종 시험이 AI 필수인데 시험1은 왜 AI를 금지하는가. 방향이 반대라서가 아니라 재는 것이 다르기 때문이다. 시험1은 AI가 꺼졌을 때 머리에 남는 능력(생성 코드의 출력 예측, 코드를 한국어로 설명하기(EiPE라 부르는 문항 유형으로, 5장에서 훈련한다), AI가 낸 오답의 채점·교정, 화공 계산의 의사코드 설계)을 따로 검증한다. AI 없이는 아무것도 못 하는 사람이 좋은 점수를 받긴 힘들다.
시험2와 최종 시험에서 AI 사용이 필수인 이유도 같은 논리다. 이 두 시험이 재는 것은 AI와 함께 제한 시간 안에 내는 실전 성과이므로, AI 없이 치르는 것은 측정 대상 밖의 행동이다. 과제에서의 AI활용은 허용이다. 위키 기록과 서브프로젝트에서 AI를 어떻게 썼는지가 오히려 학습의 일부가 될 것이다.
세 경우 모두에 공통 의무가 하나 있다. 모든 평가물에는 AI 사용내역을 첨부한다. 무엇을 물었고, 무엇을 받았고, 무엇을 고쳤는지를 적는다. (미첨부는 부정행위로 처리된다.) 아울러 프롬프트에 개인정보를 넣지 않는 것이 1주차 역할 계약에 포함된 규칙이다. 실습의 AI 접속은 교수가 운영하는 게이트웨이를 거치며, 데이터 흐름은 강의계획서에 고지되어 있다.
스스로 점검 1.3
- 시험2에서 AI 사용은 선택인가, 의무인가?
- AI 사용내역 선언문을 첨부하지 않으면 어떻게 처리되는가?
1.4 LLM은 무엇을 하는 기계인가
AI를 부리려면 내부를 전부 알 필요는 없다. 그러나 오답이 만들어지는 원리는 알아야 한다. 브레이크가 왜 밀리는지 모르는 운전자는 밀리는 순간 대응하지 못한다. 이 절에 필요한 개념은 셋뿐이다. 다음 토큰 예측, 확률적 출력, 그럴듯한 오답이다.
1.4.1 하찮은 예제: 열아홉 줄짜리 문장 생성기
출발점은 LLM이 아니라 작은 파이썬 프로그램이다. 아직 코드를 읽는 법을 배우지 않았어도 좋다. 지금은 실행하고 출력을 관찰하는 것으로 충분하며, 이 코드를 해부하는 법은 5장에서 배운다.
import random
# 아주 작은 말뭉치 — 문장 세 개가 전부다
text = ("증기압 은 온도 에 따라 커진다 . "
"증기압 은 물질 마다 다르다 . "
"온도 가 오르면 증기압 이 커진다 .")
words = text.split()
# 각 단어 뒤에 실제로 나왔던 단어를 전부 기록한다
table = {}
for w, nxt in zip(words, words[1:]):
table.setdefault(w, []).append(nxt)
word = "증기압" # 시작 단어
line = [word]
for _ in range(5): # 다음 단어를 다섯 번 이어 붙인다
word = random.choice(table[word]) # 자주 나온 단어일수록 잘 뽑힌다
line.append(word)
print(" ".join(line))
실행할 때마다 다른 문장이 나온다. 어떤 실행에서는 「증기압 은 물질 마다 다르다 .」처럼 말이 되는 문장이, 다른 실행에서는 「증기압 이 커진다 . 온도 가」처럼 도중에 끊긴 문장이 나온다. 흥미로운 점은 이것이다. 이 프로그램은 증기압이 무엇인지 모른다. 아는 것은 "어떤 단어 뒤에 어떤 단어가 왔는가"라는 기록뿐인데도 출력은 제법 그럴듯하다.
1.4.2 다음 토큰 예측과 확률적 출력
이 장난감이 하는 일을 한 문장으로 줄이면 "지금까지의 단어열을 보고, 기록에 근거해 다음 단어를 확률적으로 뽑는다"가 된다. 사람이 쓴 방대한 텍스트로 이 일을 훨씬 정교하게 하도록 훈련된 프로그램을 대형 언어 모델(large language model, LLM)이라 한다. LLM이 텍스트를 다루는 최소 단위 조각을 토큰(token)이라 하며, 단어보다 잘게 쪼개지는 경우가 많다. LLM은 직전 한 단어가 아니라 수천 토큰의 문맥 전체를 반영하고, 빈도표 대신 거대한 수치 모델로 다음 토큰의 확률을 계산한다. 규모의 차이는 압도적이지만 산출의 본성은 같다. 다음 토큰을 예측해 이어 붙인다.
코드 1-2의 random.choice가 하는 일을 LLM도 한다. 후보 토큰마다 확률을 매긴 뒤 그 분포에서 하나를 뽑는다. 그래서 같은 프롬프트를 두 번 보내면 다른 답이 올 수 있다. 고장이 아니라 설계다.
실무적으로 생각해볼 것은 두 가지다. 재생성은 공짜 재시도가 아니라 새로운 검증 대상이다. 어제 좋은 코드를 생성한 프롬프트가 오늘 다른 코드를 생성한다. 그리고 "한 번 확인했다"는 "늘 옳다"의 근거가 되지 못하므로, 확인 절차 자체를 테스트 코드로 남겨 반복 실행해야 한다. 이 습관은 2장부터 보도록 하자.
1.4.3 그럴듯한 오답
LLM이 사실이 아닌 내용을 사실처럼 자신 있게 생성하는 현상을 환각(hallucination)이라 한다. 이 이름은 오해를 부른다. 환각은 모델이 가끔 빠지는 비정상 상태가 아니다. 모델은 답의 진위를 어디에서도 조회하지 않으며, 맞는 답과 틀린 답이 같은 기제(다음 토큰 예측)로 만들어진다. 코드 1-2가 증기압의 뜻을 모른 채 증기압 문장을 만들었음을 떠올려 보자.
따라서 출력이 유창하다는 사실은 내용이 옳다는 증거가 전혀 아니다. 화학공학 데이터에서 환각은 물성 날조로 나타난다. 존재하지 않는 Antoine 상수, 출처 없는 끓는점, 그럴듯한 임계 물성. 3장에서 실제 사례를 해부하고, 5주차 실습에서는 AI가 날조한 Antoine 상수를 직접 잡아낸다.
생각해보기 실행 테스트(사이클 ⑤)를 통과하고도 살아남는 오류에는 어떤 것이 있을까. 두 가지를 상상해 보고, 각각을 잡을 방법을 제안해 보라.
스스로 점검 1.4
- 코드 1-2를 두 번 실행하면 출력이 항상 같은가? 어느 줄이 그 답을 결정하는가?
- 환각은 LLM의 고장인가, 정상 작동의 부산물인가?
1.5 15주의 지도
이 수업은 네 국면으로 진행된다. 1~4주는 훅이다. 강의노트 정리기·플래시카드 생성기·스터디 플래너로 자신의 학습 OS를 조립한다. 시간제한이 없고, 결과는 등수 없이 통과/재도전 2단계로만 처리되는 저부담 구간이다. 5~7주는 근육이다. 코드 읽기·검증 훈련과 첫 화공 도구를 다루고 시험1(AI 금지 지필)을 치른다. 8~13주는 사다리다. 화공 도구를 매주 하나씩 실기 형식으로 만들고 시험을 치른다. 13~15주는 완성이다. 서브프로젝트 발표와 최종 실습 실기, 그리고 구술 방어이다.
도구 사다리는 아홉 계단이다. ① 강의노트 정리기, ② 플래시카드 생성기, ③ 스터디 플래너, ④ 단위환산 + 물성 조회기, ⑤ Antoine 증기압 계산기, ⑥ 이성분계 T-xy 다이어그램, ⑦ 물질수지 solver, ⑧ 배관 압력강하 계산기, ⑨ 공정 데이터 대시보드. 앞의 셋은 오늘 밤 다른 과목 공부에 바로 쓰는 도구이고, 뒤의 여섯은 화공양론·유체역학과 나란히 가는 전공 도구다. 도구는 1회성 과제가 아니라 재사용 자산이다. 9장의 T-xy 다이어그램은 8장의 증기압 함수를 그대로 가져다 쓰고, 서브프로젝트는 본인 산출물 2개 이상의 재사용을 요건으로 한다.
매주 실습은 최종 시험의 축소판이다. 5주차부터 타이머가 붙고, 제한 시간은 30분에서 45·60·75·90분을 거쳐 최종 시험의 120분까지 점증한다. 같은 형식을 15번 연습한 상태로 시험장에 들어가는 것이 이 설계의 목적이다.
최종 실습시험의 루브릭은 1주차인 지금 전체가 공개된다. 작동성 14점, 코드 이해 10점, 프롬프트 전략 8점, 발표 8점. 작동성에는 검증 증빙(단위·대조값·극한값의 README 기록)이 필수 항목으로 들어 있고, 프롬프트 전략은 전체 로그로 채점된다. 전문은 부록 E에 있다.
마지막 조각이 위키다. 15주 동안 배운 것·틀린 것·만든 것을 마크다운으로 누적하는 개인 지식 저장소를 이 수업에서는 위키(위키)라 한다. 노트에는 성장단계 태그 #seed → #growing → #evergreen을 붙이고, 5·10·15주의 마일스톤 3회에 걸쳐 각 2점씩 채점된다. 채점 기준은 누적성(주당 의미 있는 커밋 1개 이상, 몰아치기는 커밋 이력에서 드러나 감점된다), 개인화 깊이, 활용도다. 매 실습의 마지막 15분은 위키 시간이며, 그날 만든 것·막힌 것·틀린 것 1건을 기록해야 퇴실한다. 2~4주에 만드는 도구들이 위키를 직접 읽고 쓰므로, 도구를 쓸수록 위키가 자라는 구조다.
생각해보기 AI가 코드를 전부 쓰는 시대에 화학공학자는 무엇으로 대체 불가능해지는가. 이 장에서 읽은 근거 말고, 본인의 근거를 두 가지 들어 보라.
실습 1: 개발환경 셋업 + 첫 바이브코딩 + 위키 개설 (120분, 타이머 없음)
준비물 Windows 노트북(전원 어댑터 포함), GitHub 계정, LMS에서 내려받은 배포 스크립트(.ps1). 스크립트는 Git, Claude Code, 게이트웨이 가상 키 설정을 일괄 구성한다. 절차 전문과 문제 해결은 부록 A에 있다. 노트북이 없거나 셋업이 끝내 실패하면 GitHub Codespaces 백업 경로(부록 A)로 전원 구제된다.
오류 메시지를 두려워할 필요가 없다. 에러는 컴퓨터를 망가뜨리지 않으며, 오류 전문을 복사해 AI에 붙여넣는 것이 이 수업의 표준 동작이다.
실습 1.1 셋업 (약 30분)
- PowerShell을 열고 배포 스크립트를 실행하라. (완료 확인: 오류 없이 "설치 완료" 메시지로 끝난다)
- 새 터미널 창을 열고
claude를 실행하라. (완료 확인: Claude Code 대화 화면이 뜬다) claude is not recognized류의 오류가 나면 창을 전부 닫고 새 PowerShell에서 다시 시도하라. (설치 직후의 기존 창은 새 명령을 모른다. 부록 A의 1번 함정)
[주의] Git Bash에서 Claude Code를 실행하지 않는다. Raw mode 오류가 난다. 이 수업의 터미널은 PowerShell 또는 Windows Terminal이다.
실습 1.2 저장소 두 개 (약 20분)
- GitHub Classroom 초대 링크로 실습 저장소를 발급받아라. (완료 확인: 본인 계정 아래 저장소가 생긴다)
- 위키 저장소를 private로 새로 만들고 교수를 collaborator로 초대하라. (완료 확인: Settings의 Collaborators 목록에 교수 계정이 보인다)
실습 1.3 첫 바이브코딩 (15분)
예제 1.2-1과 같은 절차를 이번에는 직접 돈다. 문제: 섭씨 온도를 켈빈으로 바꾸는 함수. 변환 관계는 T[K] = t[°C] + 273.15이다.
- Claude Code에 함수를 요청하라. 입력·출력 단위와 "주석은 한국어로"를 프롬프트에 명시하라. (완료 확인: 함수가 든 .py 파일이 생긴다)
- 생성 코드를 페어에게 소리 내어 한 줄씩 설명하라. (완료 확인: 페어가 "설명 안 된 줄 없음"에 동의한다)
- 0 °C를 넣어 실행하라. (완료 확인: 273.15가 나온다)
- −300 °C를 넣어 보라. 절대영도 아래의 입력을 코드가 어떻게 다루는지 관찰하고, 그대로 통과시킨다면 방어 코드를 추가로 요청하라. (완료 확인: 비물리적 입력에서 오류 또는 경고가 난다)
- 확인한 것 세 가지(단위, 대조값 273.15, 극한값 처리)를 README에 기록하라. (완료 확인: README에 검증 절 3줄이 있다)
실습 1.4 첫 커밋 (약 15분)
- 실습 저장소에 .py 파일과 README를 커밋하고 푸시하라. (완료 확인: GitHub 웹에서 파일이 보인다)
- 위키 저장소에 첫 노트를 커밋하라(오늘 만든 것·막힌 것·틀린 것 1건). (완료 확인: 위키에 커밋 1개 이상)
완성 기준: 이번 주 산출물은 "작동하는 개발환경 + 위키 repo 첫 커밋"이다.
- 새 PowerShell 터미널에서
claude가 실행된다. - 실습 저장소에 환산 함수와 검증 기록이 담긴 README가 커밋되어 있다(커밋 1개 이상).
- 위키 저장소가 private로 개설되어 교수 초대가 완료됐고, 첫 노트가 커밋되어 있다.
하나라도 미달이면 "재도전"이며, 다음 실습 전까지 완료하면 된다. 등수는 없다.
막혔는가?
- 셋업 오류: 오류 전문을 복사해 AI에게 물어라. "Windows PowerShell에서 이 오류가 났다. 원인 후보 세 가지와 각각의 확인 방법을 알려 줘."
- 함수 요청이 겉도는 경우: 단위를 빼먹지 않았는지 보라. "입력은 섭씨(°C), 출력은 켈빈(K). 함수 하나만. 주석은 한국어로."
- 푸시 인증 실패: "git push에서 이 인증 오류가 났다. 대학 실습실 Windows에서 가장 간단한 해결 순서를 단계별로 알려 줘."
퇴실 전 기록 오늘 만난 오류 중 하나를 화면 캡처와 함께 위키에 남겨라. "무엇을 하려다 → 무엇이 나왔고 → 무엇으로 해결했다" 세 줄이면 충분하다. 기록해야 퇴실이다.
요약
- S 1-1 (역할 계약) AI가 코드를 쓴다. 학생은 ① 문제 분해 ② 생성 코드 읽기·설명 ③ 화학공학적 검증을 책임진다. 설명하지 못한 코드의 이해 점수는 0점이다.
- S 1-2 (AI 협업 사이클) 문제 정의 → 분해 → 프롬프트 → 생성 코드 읽기 → 테스트 → 화공 검증. AI의 몫은 ③과 ④ 사이의 코드 생성뿐이다.
- S 1-3 (리스팅 규격) 프롬프트 / 생성 코드(점선 = 미검증) / 검증 코멘트의 3단 고정. 검증 칸이 빈 리스팅은 없다.
- S 1-4 (LLM 최소 모형) LLM은 다음 토큰을 확률적으로 예측해 이어 붙인다. 맞는 답과 틀린 답이 같은 기제로 생성되므로, 유창함은 옳음의 증거가 아니다.
| 구분 | 평가 | 재는 것 | 사이클에서의 위치 |
|---|---|---|---|
| 금지 | 시험1 | AI 없이 남는 읽기·검증·설계 능력 | ④⑤⑥을 맨손으로 |
| 필수 | 시험2·시험3 | AI와 함께 제한 시간 안에 내는 실전 성과 | ①~⑥ 전체 |
| 허용 | 과제(위키·서브프로젝트) | 누적 습관과 자기 학습 시스템 | 매주 반복되는 ①~⑥의 기록 |
이 장은 사이클의 특정 블록이 아니라 지도 전체를 폈다. 손에는 작동하는 개발환경과 위키, 그리고 사이클을 한 바퀴 돌아 본 경험이 남았다. 도구 사다리로 치면 아직 0계단이다. 다음 장부터 한 계단씩 오른다.
용어 정리
- 바이브코딩(vibe coding)
- 자연어로 의도를 말하면 AI가 코드를 작성하고, 사람이 그 코드를 읽고 검증하는 작업 방식.
- 역할 계약
- AI가 코드를 쓰고 학생이 문제 분해·생성 코드 읽기·화학공학적 검증을 책임진다는 이 수업의 명문화된 분업.
- AI 협업 사이클
- 문제 정의–분해–프롬프트–생성 코드 읽기–테스트–화공 검증의 6단계 작업 절차.
- AI 활용 규칙(3-lane)
- 평가별로 AI 사용을 금지(시험1)·필수(시험2·3)·허용(과제)으로 나눈 규칙.
- 검증 루틴 3종
- 단위 체크, 대조값 비교, 극한값 sanity의 세 가지 화공 검증 절차(3장에서 정식 도입).
- 대형 언어 모델(large language model, LLM)
- 방대한 텍스트로 다음 토큰을 예측하도록 훈련된 프로그램.
- 토큰(token)
- LLM이 텍스트를 처리하는 최소 단위 조각.
- 환각(hallucination)
- LLM이 사실이 아닌 내용을 사실처럼 자신 있게 생성하는 현상.
- 프롬프트(prompt)
- AI에게 보내는 자연어 요청문.
- 저장소(repository)
- 코드와 기록의 변경 이력을 보관하는 프로젝트 단위 폴더.
- 커밋(commit)
- 저장소에 변경 사항을 이름 붙여 기록하는 행위.
- 위키(위키)
- 배운 것·틀린 것·만든 것을 15주 동안 마크다운으로 누적하는 개인 지식 저장소.
연습문제
Q군: 개념·읽기 (AI 없이 풀라)
- Q1.1 역할 계약에서 학생이 책임지는 세 가지를 쓰고, 각각이 최종 시험 루브릭의 어느 차원(작동성·코드 이해·프롬프트 전략·발표)과 연결되는지 밝혀라.
- Q1.2 다음 네 상황을 AI 활용 규칙으로 판정하라. (a) 시험1 대비 공부 중 AI에게 기출 유형 코드를 설명시킨다 (b) 시험2 도중 막혀서 AI에게 디버깅을 요청한다 (c) 위키 정리에 AI 요약을 쓰고 선언문에 적지 않는다 (d) 시험1 답안 작성 중 스마트폰으로 AI를 조회한다.
- Q1.3 코드 1-2를 두 번 실행했을 때 출력이 다를 수 있는 이유를 코드의 특정 줄을 근거로 설명하라. LLM에서 같은 역할을 하는 것은 무엇인가.
P군: 제작·계산 (AI 사용 전제)
- P1.1 예제 1.2-1의 함수를 섭씨 입력을 받도록 바꾸는 프롬프트를 작성하고, 생성 코드를 검증 루틴 3종으로 검증하라. 검증에 쓸 대조값을 먼저 정하고 출처를 남겨라.
- P1.2 존재하지 않는 라이브러리를 일부러 요청해(예: "메탄올 물성을 phantom_props 라이브러리로 조회해 줘") 오류를 재현하라. 오류 전문과 원인, 교정 프롬프트를 위키에 기록하라.
- P1.3 예제 1.2-1의 코드를 겉보기에 그럴듯하게 틀리도록 만드는 방법을 두 가지 제시하고, 각각이 검증 루틴 3종 중 무엇에 걸리는지 밝혀라. 이어서 세 루틴을 모두 통과할 수 있는 오류를 하나 설계해 보고, 그런 오류까지 잡으려면 무엇이 더 필요한지 논하라.
위키 기록 과제 ① 이번 장 핵심을 1페이지로 정리하라(#seed 태그). ② "내가 틀렸던 것" 1건(셋업이나 첫 바이브코딩에서 만난 에러 화면 캡처와 원인). ③ 타 과목 연결 1건(예: 일반화학의 이상기체 단원과 예제 1.2-1의 연결). 이번 주 의미 있는 커밋 1개 이상이 채점 지표다.
2장 프롬프트로 함수 만들기
이번 주의 도구: ① 강의노트 정리기
다음 상황을 생각해 보자. 화공양론 중간고사가 2주 앞으로 다가왔다. 지난 6주 동안 적은 강의노트는 태블릿 메모, 사진, 종이 귀퉁이에 흩어져 있고, 어디에 무엇을 적었는지 본인도 모른다. 시험공부를 시작하려면 먼저 이 더미를 한 곳에 모아 훑어볼 수 있는 형태로 정리해야 하는데, 그 정리에만 저녁 하나가 통째로 든다.
이 정리 작업은 지루하지만 규칙이 분명하다. 규칙이 분명한 반복 작업은 프로그램의 몫이고, 그 프로그램은 이제 AI가 대신 써 준다. 남는 문제는 하나다. AI에게 무엇을, 어떤 크기로 시킬 것인가.
이 장에서 다루는 것
- 1장에서 설명한 AI 협업 사이클을 실제 도구 제작에 처음 적용한다. 이 장의 훈련 범위는 ①~⑤다.
- 변수·자료형·함수 최소 문법을 생성 코드를 읽는 데 필요한 만큼만 다룬다.
- 같은 문제에 대한 나쁜 프롬프트와 좋은 프롬프트를 나란히 놓고 비교한다.
assert문으로 생성 코드를 테스트하는 법을 연습한다.- 도구① 강의노트 정리기를 만들고, 마크다운과 .gitignore/.env 규율을 익힌다.
학습목표: 이 장을 마치면 다음을 할 수 있다
- 계산·정리 문제 하나를 함수 크기의 작은 일들로 분해할 수 있다.
- 변수·자료형·함수 개념을 사용해 생성 코드를 한 줄씩 한국어로 설명할 수 있다.
- 입력·출력·단위·예외 4요소를 갖춘 함수 단위 프롬프트를 작성할 수 있다.
assert문으로 생성 코드가 알려진 값을 재현하는지 테스트할 수 있다.- 마크다운으로 위키 노트를 작성하고 .gitignore와 .env로 키를 보호할 수 있다.
이번 주 실습에서는 §2.1의 분해표와 §2.3의 프롬프트 패턴으로 도구① 강의노트 정리기를 만든다. 매주 실습은 최종 시험(2시간 실기)의 축소판이지만, 1~4주는 1장에서 말한 저부담 구간이다.
2.1 문제 분해: 일을 함수 크기로 쪼갠다
흩어진 강의노트를 위키 노트로 바꾸는 일을 통째로 보면 막막하다. 그런데 종이에 손으로 절차를 적어 보면 다섯 조각으로 갈라진다. ① 붙여넣은 텍스트를 읽어 들인다. ② 제목이 될 문장을 찾는다. ③ 본문을 마크다운 형식으로 바꾼다. ④ 태그를 붙인다. ⑤ 파일로 저장한다. 일부러 하찮아 보일 만큼 잘게 적었지만, 이 다섯 줄이 이번 주에 만들 프로그램의 설계도가 된다.
이렇게 큰 문제를 각각 따로 해결하고 따로 확인할 수 있는 작은 일들로 나누는 것을 문제 분해(problem decomposition)라 한다. 좋은 프로그램은 개별적으로 시험해 볼 수 있는 작은 부품들의 조립품이며, 부품 하나를 만들 때마다 곧바로 실행해 확인하는 것이 오류를 빨리 찾는 길이다. 위의 다섯 조각이 각각 프로그램의 부품 하나, 즉 함수 하나가 된다.
분해의 단위를 함수 크기로 잡는 이유는 확인 가능성 때문이다. "노트를 정리해 줘"라는 요청의 결과는 옳은지 그른지 판정할 방법이 마땅치 않다. 반면 "이 텍스트에서 제목이 될 첫 문장을 골라 줘"의 결과는 눈으로 1초 만에 판정된다. AI에게 시키는 일의 크기는 사람이 검증할 수 있는 크기여야 한다. 1장에서 정한 역할 분담(코드는 AI, 검증은 학생)이 여기서 처음 작동한다.
이 역할 분담을 작업 절차로 펼친 것이 1장에서 설명한 AI 협업 사이클이다. 이 블록은 이 교재의 고정 장치로, 15장까지 모든 예제와 실습에 같은 모습으로 반복된다.
- ① 문제 정의: 무엇이 입력이고 무엇이 출력인가
- ② 분해: 함수 크기의 일들로 쪼갠다
- ③ 프롬프트: 한 번에 함수 하나씩 요청한다
- ④ 생성 코드 읽기: 한 줄씩 한국어로 설명한다
- ⑤ 테스트: 알려진 값으로 확인한다
- ⑥ 화공 검증: 단위·대조값·극한값 점검 (3장에서 정식 도입)
이 순서는 AI를 전제로 프로그래밍 입문을 재설계한 대학 수업들(UCSD의 CS1-LLM 등)이 채택한 "분해 → 프롬프트 → 코드 읽기 → 테스트" 사이클에, 화학공학적 검증 단계 ⑥을 더한 것이다. 이번 장에서는 ①~⑤를 연습하고, ⑥은 3장에서 다룬다.
분해표에는 실용적인 쓸모가 하나 더 있다. 표의 각 행이 그대로 프롬프트 하나가 되므로, 분해표를 완성한 순간 AI에게 보낼 요청 목록도 완성된다. 이번 주 실습에서는 이 표를 README에 먼저 적고 나서 코드 생성을 시작하는데, 이 순서는 최종 시험에서도 같다. 2시간 실기에서 맨 먼저 할 일이 바로 문제를 분해표로 옮기는 것이다.
2.2 함수·변수·자료형: 생성 코드를 읽기 위한 최소 문법
사이클의 4단계 "생성 코드 읽기"를 하려면 문법이 필요하다. 다만 파이썬 전체가 아니라, AI가 낸 코드에 반드시 등장하는 세 가지(변수, 자료형, 함수)만 있으면 이번 주 코드는 전부 읽힌다.
2.2.1 변수와 값
값을 가리키는 이름을 변수(variable)라 한다. 이름에 값을 붙이는 문장이 할당이다.
note_title = "화공양론 3장 물질수지" # 문자열 — 노트 제목
page_count = 17 # 정수 — 노트 분량 [쪽]
water_bp = 100.0 # 실수 — 물의 정상 끓는점 [°C]
세 줄을 하나씩 되짚어 보자. 첫 줄은 따옴표로 감싼 글자들, 즉 문자열(string)을 note_title이라는 이름에 할당한다. 둘째 줄은 정수 17을, 셋째 줄은 실수 100.0을 각각의 이름에 붙인다. 물리량을 담는 변수에는 이 교재 전체에서 단위를 주석으로 명기한다. 나중에 단위 검증의 대조 대상이 코드 안에 있어야 하기 때문이다.
2.2.2 자료형: 값에는 종류가 있다
값이 속한 종류를 자료형(data type)이라 한다. 위에서 만난 세 가지가 파이썬의 기본 자료형으로, 문자열은 str, 정수는 int, 소수점이 있는 수는 float에 속한다. 어느 형인지 헷갈리면 type()으로 물어보면 된다.
함정은 숫자처럼 보이는 문자열이다. "17"은 따옴표 안에 있으므로 수가 아니라 문자열이고, 계산에 쓰려면 변환해야 한다. 그리고 이 변환은 단위 표기가 섞이는 순간 실패한다. 실제로 실행해 보자.
>>> type("17")
<class 'str'>
>>> float("25.0")
25.0
>>> float("25.0 °C")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: could not convert string to float: '25.0 °C'
마지막 에러는 이번 주 실습에서 실제로 만나게 된다. 강의노트는 문자열이고, 그 안의 "25.0 °C" 같은 표기는 사람 눈에만 숫자다. 에러 메시지의 마지막 줄에 원인이 그대로 적혀 있으므로, 에러가 나면 이 줄부터 읽는 습관을 들이자.
2.2.3 함수: 이름 붙인 작업 한 덩어리
문장 여러 개를 묶어 이름을 붙인 것을 함수(function)라 한다. §2.1의 분해표에서 쪼갠 "일 하나"가 코드에서는 함수 하나로 나타난다. 정의와 호출을 한 번에 보자.
def celsius_to_kelvin(temp_c): # temp_c: 매개변수 [°C]
"""섭씨 온도를 켈빈 온도로 변환한다."""
temp_k = temp_c + 273.15 # 식 (2.1) [K]
return temp_k # 반환값 [K]
t_boil = celsius_to_kelvin(100.0) # 100.0: 인자 [°C]
print(t_boil) # 373.15가 출력된다
여기서 쓴 변환식은 켈빈 온도와 섭씨 온도의 정의 관계다.
TK = TC + 273.15 (2.1)
TK는 켈빈 온도[K], TC는 섭씨 온도[°C]이고, 273.15는 두 눈금 사이의 정의된 오프셋이다. 코드로 돌아가면, def 줄의 괄호 안 이름 temp_c를 매개변수(parameter)라 하고, 호출할 때 괄호에 넣어 주는 값 100.0을 인자(argument)라 한다. 함수가 호출되면 인자가 매개변수라는 변수에 할당되고, 함수 몸통이 실행된 뒤 return 뒤의 값, 즉 반환값(return value)이 호출한 자리로 돌아온다. # 뒤의 설명글은 주석(comment)으로, 실행에는 관여하지 않지만 코드를 읽는 사람에게 단위와 의도를 알려 준다.
return이 없는 함수는 어떻게 될까. 화면에 print로 출력만 하는 함수도 문법상 유효하지만, 그 결과를 변수에 담으면 None이라는 특별한 값이 들어온다. None은 "반환값이 없음"을 나타내는 고유한 자료형의 값으로, 문자열 'None'과는 다르다. 출력된 숫자는 눈에만 보일 뿐 다음 계산에 쓸 수 없으므로, 계산 함수를 요청할 때는 반드시 "값을 반환하게" 요구해야 한다. 이것이 §2.3 프롬프트 4요소 중 '출력'이 존재하는 이유다.
스스로 점검 (해답은 권말)
type("3.14")의 결과는 무엇인가?- 코드 2-3에서 매개변수와 인자는 각각 무엇인가?
print만 하고return이 없는 함수의 결과를 변수에 담으면 무슨 값이 들어오는가?
2.3 함수 단위 프롬프트: 한 번에 부품 하나
문법을 갖췄으니 사이클의 3단계로 간다. 프롬프트의 품질은 결과 코드의 품질을 직접 결정하는데, 초보자의 프롬프트가 실패하는 가장 흔한 형태는 "너무 큰 요청"이다. 실제로 어떻게 실패하는지 먼저 보자.
프롬프트 (나쁜 예)
온도 변환기 만들어 줘.
print("=== 만능 온도 변환기 ===")
mode = input("1) C→F 2) F→C 3) C→K 4) K→C 선택: ")
if mode == "1":
temp_f = float(input("섭씨 온도: ")) * 9 / 5 + 32 # [°F]
print("화씨:", temp_f)
# … (이후 40여 줄: 화씨·랭킨 분기, 메뉴 반복 루프 생략)
✔ 판정: 함수가 아니라 메뉴 달린 프로그램 전체가 왔다. 요청에 단위도 방향도 없어 AI가 화씨까지 알아서 넣었고, 이 중 어느 줄도 다음 주 도구에서 재사용할 수 없다. 부품이 아니라 완제품을 받은 것이 문제다 → 함수 단위로 다시 요청한다.
고치는 방법은 요청을 분해표의 한 칸 크기로 줄이고, 네 가지를 명시하는 것이다. 무엇이 들어가고(입력), 무엇이 나오고(출력), 단위는 무엇이며(단위), 이상한 입력에는 어떻게 반응할지(예외). 이 입력·출력·단위·예외 4요소가 이 교재의 표준 프롬프트 패턴이다.
예제 2.3-1 온도 변환 함수를 프롬프트로 만들기
문제 섭씨 온도를 켈빈으로 변환하는 함수를 AI에게 요청해 만들고, 그 결과를 검증하라. 절대영도보다 낮은 입력은 거부해야 한다.
풀이 전략 코드 작성은 AI에게 맡기고, 4요소를 채운 프롬프트 작성과 결과 판정은 직접 한다. 판정 기준은 미리 정해 둔다. 어는점 0 °C → 273.15 K, 끓는점 100 °C → 373.15 K가 나와야 한다.
프롬프트
섭씨 온도를 켈빈으로 변환하는 파이썬 함수를 만들어 줘. 입력은 섭씨 온도 하나(실수, °C), 출력은 켈빈 온도(실수, K)를 return으로 반환. 절대영도인 -273.15 °C보다 낮은 입력이 오면 ValueError를 일으켜 줘. 주석은 한국어로, 단위도 주석에 적어 줘.
def celsius_to_kelvin(temp_c):
"""섭씨 온도[°C]를 켈빈 온도[K]로 변환한다."""
ABS_ZERO_C = -273.15 # 절대영도 [°C], 정의값
if temp_c < ABS_ZERO_C:
raise ValueError("절대영도보다 낮은 온도: " + str(temp_c) + " °C")
return temp_c + 273.15 # 켈빈 온도 [K]
✔ 판정: 4요소가 모두 반영됐다. 절대영도가 이름 있는 상수 ABS_ZERO_C로 뽑혀 있고, 범위 방어가 return보다 먼저 온다. 다만 이 판정은 아직 눈으로 읽은 것뿐이다. 실행 확인은 아래에서 한다.
- 단위: 입력 [°C], 출력 [K]로 식 (2.1)과 일치하며, 주석에 명기됨
- 대조값: 0 °C → 273.15 K, 100 °C → 373.15 K 반환 확인 (§2.4의 테스트로 실행 검증)
- 극한값: -273.15 °C → 0.0 K, 그보다 낮으면 ValueError 발생 확인
분석 나쁜 프롬프트와 같은 문제를 요청했는데 결과는 6줄짜리 재사용 가능한 부품이 됐다. 차이는 전부 프롬프트의 4요소에서 나왔다. 위의 세 줄 점검은 3장에서 검증 루틴 3종으로 정식화된다. 지금은 형식부터 몸에 익혀 보자. What-if: 프롬프트에서 예외 요구 문장만 지우고 재생성해, 범위 방어가 사라지는지 확인해 보라.
2.4 테스트: 생성 코드를 믿지 않는 방법
코드 2-5는 그럴듯하다. 그러나 "그럴듯하다"는 느낌만으로는 판정이 되지 않는다. 사이클의 5단계는 답을 이미 아는 입력을 넣어 기계가 판정하게 만드는 것이고, 파이썬에서 가장 짧은 도구가 assert 문이다. assert 조건, "메시지"는 조건이 참이면 조용히 지나가고, 거짓이면 메시지와 함께 프로그램을 멈춘다.
# test_convert.py — 답을 아는 입력으로 생성 함수를 검증한다
from convert import celsius_to_kelvin
assert celsius_to_kelvin(0.0) == 273.15, "물의 어는점 대조 실패" # [K]
assert celsius_to_kelvin(100.0) == 373.15, "물의 끓는점 대조 실패" # [K]
assert celsius_to_kelvin(-273.15) == 0.0, "절대영도 대조 실패" # [K]
print("테스트 3개 통과")
세 케이스의 선택에 논리가 있다. 어는점과 끓는점은 일반화학에서 이미 아는 대조값이고, 절대영도는 함수가 허용하는 가장 낮은 극한값이다. 테스트 케이스는 대개 아는 값과 극한값 두 곳에서 나온다.
순서도 중요하다. 예제 2.3-1의 풀이 전략에서 판정 기준(0 °C → 273.15 K)을 코드를 받기 전에 정해 둔 것을 눈여겨보라. 생성 코드를 먼저 보고 나서 기준을 정하면, 눈앞의 출력에 맞춰 기준이 끌려가는 일이 생긴다. 판정 기준은 AI가 아니라 사람이 정해야 하고, 그 형식이 바로 미리 적어 둔 테스트다.
이 테스트가 실제로 무엇을 잡아내는지 보자. 프롬프트에서 조건을 흐리면 AI는 자주 다음과 같은 코드를 내놓는다.
0.15 K는 사소해 보이지만, 증기압처럼 온도에 민감한 물성 계산의 입력으로 들어가면 오차가 증폭된다. 8장에서 이 함수를 실제로 재사용할 때 다시 확인하게 된다.
스스로 점검 (해답은 권말)
assert의 조건이 거짓이면 무슨 일이 일어나는가?- 테스트 3개가 모두 통과하면 이 함수가 옳다고 결론지을 수 있는가? 한 문장으로 답하라.
2.5 마크다운과 저장소 규율: .gitignore와 .env
이번 주 도구의 출력 형식이 마크다운이므로, 만들기 전에 형식부터 읽을 줄 알아야 한다. 몇 개의 기호만으로 문서 구조를 표시하는 텍스트 표기법을 마크다운(markdown)이라 한다. 위키의 모든 노트가 이 형식이고, 이 교재의 README·기록 과제도 전부 마크다운으로 쓴다.
| 표기 | 의미 | 예 |
|---|---|---|
#, ## | 제목 (개수 = 수준) | # 물질수지 기초 |
**굵게** | 강조 | **증기압** |
- | 목록 항목 | - 정상상태 가정 |
`코드` | 코드·명령 표기 | `assert` |
[[링크]] | 다른 노트로 연결 (Obsidian 문법) | [[화공양론]] |
정리기가 만들어 낼 노트 한 장의 목표 형태는 다음과 같다. 이 구조(핵심 정리 / 내가 틀렸던 것 / 타 과목 연결)는 위키 채점 기준의 3항과 같다.
# 물질수지 기초
#seed
## 오늘 배운 것
- **물질수지**: 축적 = 입력 - 출력 (반응이 없을 때)
- 정상상태에서는 축적 항이 0이다.
## 내가 틀렸던 것
- bypass 흐름을 recycle로 착각했다.
## 연결
- [[화공양론]] 3장 · [[유체역학]] 질량 보존
저장 규율이 하나 남았다. 1주차에 발급받은 게이트웨이 가상 키는 비밀값이다. 키를 코드나 노트에 적은 채 커밋하면 저장소 이력에 남고, 이력에 남은 비밀은 나중에 지우기가 어렵다. 그래서 비밀값은 .env라는 별도 파일에 두고, 그 파일 자체를 Git이 추적하지 못하게 막는다. 추적 제외 목록이 .gitignore 파일이다.
# .gitignore
.env # 가상 키가 든 파일 — 커밋 금지
__pycache__/ # 파이썬이 만드는 캐시 폴더
*.pyc
커밋 전에 확인하는 습관 하나면 충분하다. git status 출력에 .env가 보이면 .gitignore가 작동하지 않는 것이니 멈추고 원인을 찾는다. 스타터 repo에는 이 .gitignore가 이미 들어 있다. 실습 마지막 단계에서 직접 확인한다.
생각해보기
완성된 강의노트 정리기가 "믿을 만하다"고 판단할 방법을 두 가지 제안하라. 하나는 이 장에서 배운 테스트를 사용하고, 다른 하나는 테스트 없이 사람이 할 수 있는 방법이어야 한다.
실습 2 도구①: 강의노트 정리기 (타이머 없음)
입력은 텍스트 붙여넣기 한정이다. OCR·PDF 처리는 금지 범위이니 시도하지 않는다(숨은 난도가 커서 실습 2시간을 다 쓰게 된다). 타이머는 없고, 결과는 통과/재도전 2단계로만 처리한다. 재도전은 다음 실습 전까지 허용된다.
준비물
- 1주차에 셋업한 개발환경 (PowerShell에서
claude실행 확인, Git Bash 금지) - 실습 2 스타터 repo (파싱 계층 제공: 텍스트 읽기 함수와 저장 함수는 이미 완성돼 있다)
- 본인 전공 과목 강의노트 텍스트 3편 (붙여넣기 가능한 형태로 준비)
def read_pasted_text():
"""붙여넣은 강의노트 텍스트를 문단 리스트로 돌려준다."""
def save_note(filename, markdown_text):
"""완성된 마크다운 노트를 wiki/ 폴더에 저장한다."""
과제
- 실습 2.1 스타터 repo를 클론하고
python organizer.py를 실행하라. (성공 시 예제 노트가 변환 없이 그대로 출력된다) - 실습 2.2 정리기의 일을 §2.1처럼 분해해 README에 분해표(입력 → 처리 단계들 → 출력)로 기록하라. (각 행이 함수 하나에 대응해야 한다)
- 실습 2.3 첫 부품, 즉 문단 리스트에서 제목을 골라내는 함수를 4요소 프롬프트로 생성하라. 완료 후 커밋하라. (예제 노트에서 제목 한 줄이 뽑히면 완료)
- 실습 2.4 본문을 코드 2-7 형태의 마크다운으로 바꾸는 함수를 생성하고,
assert테스트 3개를 작성해 통과시켜라. 완료 후 커밋하라. (실행 시 "테스트 3개 통과"가 출력되면 완료) - 실습 2.5 본인 강의노트 3편을 변환해 위키에 저장하고 커밋하라. (wiki/ 폴더에 .md 파일 3개가 생기면 완료)
- 실습 2.6
git status로.env가 추적 목록에 없는지, 커밋 이력에 키가 없는지 확인하라. (둘 다 없어야 완료)
완성 기준 (통과 / 재도전)
- 정리기 v1이 작동한다: 예제 노트를 넣으면 마크다운 노트가 나온다
- 본인 전공 노트에서 만든 위키 노트 3개가 저장소에 있다
assert테스트 3개 통과 + README에 분해표 기록- 의미 있는 커밋 3회 이상 (실습 2.3 / 2.4 / 2.5 각 1회 이상)
- .env 미추적 확인 완료
막혔는가? 힌트 프롬프트 (정답 코드가 아니다)
- 에러가 나면: "다음 에러 메시지의 원인 후보를 3개 제시하고, 각각 확인하는 방법을 알려 줘: (에러 전문 붙여넣기)"
- 생성 코드가 안 읽히면: "이 함수에 한 줄씩 한국어 주석을 달고, 각 변수의 자료형을 알려 줘: (코드 붙여넣기)"
- 테스트가 안 떠오르면: "이 함수를 검증할 테스트 케이스를 제안해 줘. 정상 입력 2개와 극한 입력 1개, 각각 왜 그 값인지 이유도 함께."
퇴실 전 기록
실습 종료 15분은 위키 시간이다. 오늘 만든 것, 막힌 것, 틀렸던 것 각 1건을 정리기로 노트화해 커밋해야 퇴실한다. 방금 만든 도구를 자기 기록에 바로 쓰는 것이 이 수업의 순환 구조다.
요약
- S 2-1 함수 단위 프롬프트 4요소: 입력 · 출력 · 단위 · 예외. 비운 요소는 AI가 대신 정한다.
- S 2-2 AI 협업 사이클: ① 문제 정의 → ② 분해 → ③ 프롬프트 → ④ 생성 코드 읽기 → ⑤ 테스트 → ⑥ 화공 검증.
- S 2-3 식 (2.1) TK = TC + 273.15. 상수의 정밀도까지 프롬프트에 명시한다.
| 항목 | 나쁜 프롬프트 | 좋은 프롬프트 |
|---|---|---|
| 요청 단위 | 프로그램 전체 ("변환기 만들어 줘") | 함수 하나 (분해표 한 칸) |
| 단위·정밀도 | 없음(AI가 재량으로 결정) | °C → K, 상수 273.15 명시 |
| 예외 처리 | 없음 | 절대영도 미만 → ValueError |
| 결과 확인 | 눈으로 "그럴듯한지" 본다 | assert로 대조값 3개 자동 판정 |
이 장에서 AI 협업 사이클의 ①~⑤가 처음으로 한 바퀴 돌았다. 문제를 함수 크기로 쪼개고, 4요소 프롬프트로 부품을 받아, 읽고, 테스트로 판정했다. 도구 사다리의 첫 칸인 정리기 v1도 완성했다. 그러나 273.15 자리에 273이 들어간 것을 잡아낸 근거는 문법 지식이 아니라 어는점이라는 화학의 사실이었다. 이것이 사이클의 마지막 블록 ⑥이 필요한 이유이고, 3장의 주제다.
용어 정리
- 문제 분해(problem decomposition)
- 큰 문제를 각각 따로 해결하고 따로 확인할 수 있는 작은 일들로 나누는 것.
- 변수(variable)
- 값을 가리키는 이름.
- 자료형(data type)
- 값이 속한 종류. 파이썬 기본형으로 str, int, float가 있다.
- 문자열(string)
- 따옴표로 감싼 글자들의 값. 숫자처럼 보여도 따옴표 안이면 문자열이다.
- 함수(function)
- 문장 여러 개를 묶어 이름을 붙인 작업 한 덩어리.
- 매개변수(parameter)
- 함수 정의의 괄호 안 이름. 호출 시 인자가 이 변수에 할당된다.
- 인자(argument)
- 함수를 호출할 때 괄호에 넣어 전달하는 값.
- 반환값(return value)
- 함수가 return으로 호출한 자리에 돌려주는 값. 없으면 None이 돌아온다.
- 주석(comment)
- # 뒤에 쓰는 설명글. 실행되지 않으며, 이 교재에서는 단위 명기에 쓴다.
- 마크다운(markdown)
- #, -, ** 같은 기호로 문서 구조를 표시하는 텍스트 표기법.
연습문제
Q군: 개념·읽기 (AI 없이 푼다)
- Q2.1 ● 다음 코드의 출력을 예측하라. 그 다음 각 변수의 자료형을 쓰라.
mass = "150.5" # [g] print(type(mass)) volume = 2.0 # [L] print(mass + volume) - Q2.2 ● 코드 2-5의 목적과 동작을 한국어 2~3문장으로 설명하라.
ABS_ZERO_C가 이름 있는 상수로 분리된 이유를 반드시 포함하라. - Q2.3 ●● AI가 낸 다음 답안을 채점하고 교정하라. 틀린 곳을 모두 찾고, 코드 2-6 형식의 테스트 중 어느 줄이 이를 잡아내는지 밝혀라.
def kelvin_to_celsius(temp_k): print(temp_k - 273) # [°C]
P군: 제작 (AI 사용 전제)
- P2.1 ● 예제 2.3-1과 같은 절차로, 켈빈을 섭씨로 변환하는 함수를 4요소 프롬프트로 만들어라. 테스트 3개(대조값 2 + 극한값 1)를 함께 작성하고 통과시켜라.
- P2.2 ●● (메타인지) 예제 2.3-1의 프롬프트를 일부러 나쁘게 바꾸는 방법을 두 가지 설계하고, 각각 어떤 결함이 있는 코드가 나올지 예측한 뒤 실제로 재생성해 예측과 비교하라.
- P2.3 ●●● 강의노트 정리기에 "핵심 용어 자동 강조" 기능을 추가하라. 노트에 자주 나온 전공 용어를
**굵게**로 감싸는 함수를 분해표에 추가하고, 프롬프트로 생성한 뒤, 무엇을 근거로 "자주"를 판정했는지 README에 기록하라. 테스트 2개 이상을 포함하라.
위키 기록 과제 (이번 주 의미 커밋 ≥1이 채점 지표다)
- 이 장의 핵심(사이클 6단계 + 프롬프트 4요소)을 1페이지로 정리하라(
#seed태그). - 이번 주에 "내가 틀렸던 것" 1건을 에러 메시지 전문과 함께 기록하라.
- 타 과목 연결 1건: 이번 주 함수 하나가 화공양론·유체역학의 어느 계산에 쓰일지 한 문단으로 적어라.
3장 LLM은 어떻게 틀리는가
이번 주의 도구: ② 플래시카드 생성기
다음 상황을 그려 보자. 화공양론 쪽지시험을 사흘 앞두고, 지난주에 만든 강의노트 정리기로 정리해 둔 위키 노트를 AI에게 넘기며 부탁한다. "이 노트로 암기 카드를 만들어 줘." 30초 만에 카드 40장이 왔다. 문장은 매끄럽고, 표는 반듯하고, 숫자에는 단위까지 붙어 있다.
그중 한 장의 뒷면에 이렇게 적혀 있었다. "물의 임계온도는 350 °C이다." 카드를 며칠 외운 학생은 시험에서 이 값을 그대로 적었다가 감점을 받는다. 물의 임계온도는 350 °C가 아니라 약 374 °C이기 때문이다. AI가 지어낸 숫자였지만, 카드의 어느 곳에도 "이건 확실치 않음"이라는 표시는 없었다.
이 장의 질문은 하나다. 매끄러운 카드와 옳은 카드를 어떻게 구별하는가. 2장 끝에서 273과 273.15를 갈라 준 것은 문법이 아니라 어는점이라는 화학의 사실이었다. 그때 예제 곁에 세 줄로 붙여 두었던 점검을, 이 장에서 정식 절차로 세운다.
이 장에서 다루는 것
- 2장에서 미리 맛본 세 줄 점검을 검증 루틴 3종으로 정식화한다. 이 장의 훈련 초점은 AI 협업 사이클의 블록 ⑥이다.
- 환각을 화공 맥락의 세 유형(물성 날조·단위 오류·자신만만한 오답)으로 해부하고, 각 유형의 실제 오류를 지면에 올린다.
- 물성 대조값의 두 출처, NIST WebBook과 CoolProp을 소개한다(코드로 직접 조회하는 것은 5장이다).
- Antoine 식을 검증 대상으로만 미리 다룬다(물리적 의미는 8장에서 배운다).
- 도구② 플래시카드 생성기를 만들고, 생성된 카드에 날조된 사실이 없는지 대조값으로 검사한다.
학습목표: 이 장을 마치면 다음을 할 수 있다
- LLM의 환각이 물성 날조·단위 오류·자신만만한 오답으로 나타나는 방식을 구분해 설명할 수 있다.
- 검증 루틴 3종(단위 체크·대조값·극한값 sanity)을 정의하고, 각각이 어떤 오류를 잡는지 말할 수 있다.
- NIST WebBook에서 물성 상수를 조회해, AI가 제시한 값의 날조 여부를 대조값으로 판정할 수 있다.
- 상관식을 유효범위 밖에 적용했을 때 생기는 외삽 오차를 극한값 검사로 잡아낼 수 있다.
- 위키 노트로부터 플래시카드 덱을 만들고, 생성된 카드를 대조값으로 검증할 수 있다.
이번 주 실습에서는 §3.2의 검증 루틴과 §3.3의 대조 기법으로 도구② 플래시카드 생성기를 만든다. 검증 루틴을 자기 학습 도구에 먼저 적용해 두면, 5주차에 같은 루틴을 화공 도구(도구④)에 옮겨 "AI가 날조한 Antoine 상수"를 직접 잡아낼 때 형식이 이미 손에 익어 있다. 이번 주도 1장에서 말한 저부담 구간이다.
3.1 유창함은 옳음이 아니다: 환각의 세 얼굴
1장에서 LLM을 최소한으로 해부하며 세 가지를 확인했다. LLM은 다음 토큰을 확률적으로 예측해 이어 붙이고, 그래서 같은 프롬프트에도 다른 답을 내며, 맞는 답과 틀린 답이 같은 기제로 만들어진다. 마지막 사실이 이 장의 출발점이다. 출력이 유창하다는 것은 "그럴듯한 토큰을 잘 이었다"는 뜻일 뿐, 내용이 옳다는 증거가 아니다.
근거 없이 그럴듯하게 지어낸 출력을 환각(hallucination)이라 한다. 환각은 LLM의 고장이 아니라 정상 작동의 부산물이다. 모델은 "이 문맥 다음에 올 법한 문장"을 만들 뿐, 그 문장이 세계와 맞는지는 검사하지 않는다. 도입부의 카드가 정확히 그랬다. "물의 임계온도는 350 °C이다"는 물의 임계온도에 대한 문장으로서 형식이 완벽했고, 그래서 모델은 그것을 내놓았다. 값이 맞는지는 모델의 관심사가 아니었다.
화학공학 데이터에서 환각은 세 가지 얼굴로 나타난다. 이 셋을 구분해 두어야 어떤 검증이 어떤 오류를 잡는지 보인다.
3.1.1 물성 날조: 없는 값을 자신 있게 만든다
첫째 얼굴은 물성 날조다. 존재하지 않는 물성 상수, 출처 없는 끓는점, 그럴듯한 임계 물성을 모델이 지어낸다. 도입부 카드의 "350 °C"가 여기에 해당한다. 위험한 점은 날조된 값이 터무니없어 보이지 않는다는 것이다. 물의 임계온도가 3500 °C였다면 누구나 의심하겠지만, 350 °C는 참값 374 °C와 그리 멀지 않아 눈으로는 걸러지지 않는다. 그럴듯한 크기의 오답이 특히 걸러 내기 어렵다.
3.1.2 단위 오류: 개념은 맞고 단위가 틀린다
둘째 얼굴은 단위 오류다. 이 경우 모델은 옳은 개념을 쓰지만 단위·로그 밑·환산에서 미끄러진다. 압력을 mmHg로 계산해 놓고 bar라 이름 붙이거나, 상용로그(log10)로 정의된 식을 자연로그(ln)로 계산하는 식이다. 결과는 자릿수가 통째로 어긋나기도 하지만, 때로는 크기가 비슷해 물성 날조보다 더 잘 숨는다. 이 얼굴을 잡는 검증이 뒤에 나올 단위 체크다.
3.1.3 자신만만한 오답: 어조는 정답 여부와 무관하다
셋째 얼굴은 앞의 둘을 더 위험하게 만드는 성질이다. LLM은 틀린 답도 맞는 답과 똑같이 확신에 찬 문장으로 내놓는다. 출력의 어조는 정답 여부와 아무 상관이 없다. 도입부 카드에는 "아마도"나 "확인 필요" 같은 표시가 없었고, 5주차에 만날 사례에서는 AI가 날조한 Antoine 상수 뒤에 "출처: NIST WebBook"이라는 가짜 출처까지 붙인다. 사람이라면 모르는 것 앞에서 머뭇거리지만, 모델의 문장은 아는 것과 모르는 것을 같은 톤으로 서술한다. 그래서 "AI가 확신하는가"는 판정 기준으로 삼기 어렵다. 판정은 어조가 아니라 대조에서 나와야 한다.
스스로 점검 (해답은 권말)
- 환각은 LLM의 고장인가, 정상 작동의 부산물인가? 한 문장으로 답하라.
- "물의 임계온도는 3500 °C이다"와 "물의 임계온도는 350 °C이다" 중 어느 쪽이 더 위험한 오답인가? 이유를 쓰라.
- AI가 확신에 찬 어조로 답했다는 사실은 그 답의 옳음에 대해 무엇을 말해 주는가?
3.2 검증 루틴 3종: 역할 분담을 절차로
1주차의 역할 분담은 짧았다. 코드는 AI가 쓰고, 검증은 학생이 한다. 그런데 "검증한다"는 말은 그 자체로는 절차가 아니다. 무엇을, 어떤 순서로 확인하는지 정해져 있지 않으면 검증은 "한 번 훑어봤다"로 미끄러진다. 그래서 이 수업은 검증을 세 개의 고정된 질문으로 못 박는다. 단위가 맞는가, 아는 값과 맞는가, 극한에서 말이 되는가. 이 셋을 검증 루틴 3종이라 하고, 15장까지 모든 예제와 실습에서 같은 순서·같은 형식으로 반복한다.
- ① 문제 정의: 무엇이 입력이고 무엇이 출력인가
- ② 분해: 함수 크기의 일들로 쪼갠다
- ③ 프롬프트: 한 번에 함수 하나씩 요청한다
- ④ 생성 코드 읽기: 한 줄씩 한국어로 설명한다
- ⑤ 테스트: 알려진 값으로 확인한다
- ⑥ 화공 검증: 단위 체크 · 대조값 · 극한값 sanity (이 장에서 정식 도입)
⑤ 테스트와 ⑥ 화공 검증이 어떻게 다른지부터 갈라 두자. 테스트는 코드가 "내가 정한 대로 동작하는가"를 묻고, 화공 검증은 "그 대로가 물리적으로 옳은가"를 묻는다. 2장의 assert는 273.15를 기대값으로 넣어 273을 잡아냈지만, 기대값 자체를 273으로 잘못 정했다면 테스트는 조용히 통과한다. 테스트가 통과한 뒤에도 검증 루틴 3종이 남는 이유가 여기 있다.
3.2.1 단위 체크: 입력·출력 단위가 식과 맞는가
첫째 루틴은 단위 체크다. 함수에 들어가는 값과 나오는 값의 단위가 식이 요구하는 단위와 일치하는지 확인한다. 이 확인이 가능하려면 단위가 코드 안에 적혀 있어야 하므로, 이 교재는 물리량 변수마다 단위를 주석으로 남긴다. T = 373.15 # K처럼 적는다. 단위가 적혀 있지 않으면 검증하기 어렵다.
상관식을 다룰 때는 단위 체크가 네 항목으로 늘어난다. 상관식은 겉보기가 같아도 정의가 제각각이기 때문이다. 같은 Antoine 식이라도 어떤 자료는 로그 밑을 log10으로, 어떤 자료는 ln으로 쓰고, 압력을 bar로 주기도 mmHg로 주기도 한다. 그래서 상관식의 상수를 받으면 로그 밑 · 압력 단위 · 온도 단위 · 유효범위 네 가지를 먼저 확인한다. 이 네 항목을 이 교재에서는 상관식 상수 표에 늘 함께 적는다.
3.2.2 대조값: 아는 값 한 점과 비교한다
둘째 루틴은 대조값 비교다. 계산 결과를 이미 알려진 참값 한 점과 맞대어 본다. 물이 1 atm에서 100 °C에 끓는다는 사실 하나만 있어도, 100 °C의 증기압 계산이 1.013 bar 근처로 나오는지 확인할 수 있다. 대조값이 하는 일은 물성 날조와 자신만만한 오답을 동시에 걸러 내는 것이다. AI의 어조가 아무리 확신에 차 있어도, 값이 참값과 어긋나면 그 답은 채택하지 않는다.
대조값은 어디서 얻는가. 두 곳을 이 장에서 소개하고, 코드로 조회하는 법은 5장에서 익힌다.
NIST Chemistry WebBook은 미국 국립표준기술원(NIST)이 운영하는 공개 물성 데이터베이스다. 웹 브라우저에서 물질을 찾아 상변화 데이터(phase change data)를 열면, 끓는점·임계 물성과 함께 Antoine 상수까지 출처가 명기된 채로 나온다. 조회 절차는 다음과 같다.
- 브라우저에서
webbook.nist.gov/chemistry로 간다. - 이름이나 화학식으로 물질을 검색한다(예: water, H2O).
- 물질 홈페이지에서 "Other data available"의 "Phase change data"를 연다.
- 표에서 필요한 값을 읽고, 유효범위와 출처를 함께 적어 둔다.
CoolProp은 순수 유체의 열역학 물성을 계산해 주는 오픈소스 라이브러리다. NIST WebBook이 사람이 눈으로 찾는 웹 조회라면, CoolProp은 코드가 함수 호출로 값을 받아 오는 조회다. 자동화된 도구 안에서는 웹을 사람이 뒤질 수 없으므로, 5장부터는 물성값을 CoolProp으로 조회한다. 지금 기억할 것은 하나다. 물성값은 AI에게 묻지 않는다.
3.2.3 극한값 sanity: 극한에서 물리가 말이 되는가
셋째 루틴은 극한값 sanity다. 입력을 물리적 극단으로 밀어 넣고, 결과가 상식과 같은 방향인지 본다. 온도를 낮추면 증기압도 낮아져야 하고, 압력을 낮추면 끓는점도 내려가야 한다. 방향이 거꾸로면 대조값을 따질 것도 없이 코드가 틀린 것이다.
극한값 검사가 특히 날카롭게 걸리는 지점이 상관식의 유효범위 경계다. 경험식은 특정 온도·압력 구간의 데이터에 맞춰 만든 곡선이라, 그 구간을 벗어나 값을 구하면 오차가 급격히 커진다. 유효범위 밖에서 값을 구하는 일을 외삽(extrapolation)이라 한다. §3.3의 예제에서 이 외삽 오차를 숫자로 직접 본다.
스스로 점검 (해답은 권말)
- 테스트(⑤)를 통과한 코드에도 화공 검증(⑥)이 남는 이유를 한 문장으로 답하라.
- 상관식 상수를 받았을 때 먼저 확인할 네 항목은 무엇인가?
- 물성값을 AI에게 직접 묻는 대신 무엇을 하라고 했는가?
3.3 물성 날조를 잡아내는 법: Antoine 상수 사례
세 루틴을 실제 물성에 걸어 보자. 소재는 증기압이다. 액체가 자신의 기체와 평형을 이루는 압력을 증기압(vapor pressure, p*)이라 하고, 온도가 오르면 증기압도 오른다. 증기압을 온도의 함수로 근사하는 대표적 경험식이 Antoine 식이다.
log10 p* = A − B / (T + C) (3.1)
여기서 A, B, C는 물질마다 다른 상수이고, p*는 증기압, T는 온도다. 이 식의 물리적 의미와 유도는 8장에서 다룬다. 지금은 검증의 대상으로만 쓴다. 이 절에서 배울 것은 Antoine 식을 푸는 법이 아니라, AI가 이 식으로 낸 값을 어떻게 걸러 내는가다.
NIST WebBook에서 물의 상수를 조회하면 온도 구간별로 여러 벌이 나온다. 그중 두 벌을 표 3-1에 옮겼다. 표에 로그 밑·압력 단위·온도 단위·유효범위 네 항목이 모두 적혀 있음을 눈여겨보라. 이것이 §3.2.1에서 말한 상관식 상수 표의 형식이다.
| 유효범위 [K] | A | B | C | 원자료 |
|---|---|---|---|---|
| 379 – 573 | 3.55959 | 643.748 | −198.043 | Liu and Lindsay, 1970 |
| 344 – 373 | 5.08354 | 1663.125 | −45.622 | Bridgeman and Aldrich, 1964 |
예제 3.3-1 100 °C 물의 증기압: 외삽 오차를 잡는다
문제 AI에게 Antoine 식으로 물의 증기압을 계산하는 함수를 만들게 하고, T = 373 K(100 °C)의 결과를 검증 루틴 3종으로 검사하라. 표 3-1의 상수를 쓴다.
풀이 전략 계산 함수는 AI에게 맡기고, 상수 선택·유효범위 확인·대조값 판정은 직접 한다. 판정 기준은 코드를 받기 전에 정해 둔다. 물은 1 atm에서 100 °C에 끓으므로 100 °C의 증기압은 1.013 bar 근처여야 한다.
프롬프트
Antoine 식 log10(p*) = A − B/(T + C)로 증기압을 계산하는 파이썬 함수를 만들어 줘. 입력은 온도(실수, K)와 상수 A, B, C, 출력은 증기압(실수, bar)을 return으로. 로그 밑은 10, 주석은 한국어로 단위와 함께 달아 줘.
def water_vapor_pressure(T, A, B, C): # T: 온도 [K]
"""Antoine 식으로 증기압을 계산한다. 반환 단위 [bar]."""
return 10 ** (A - B / (T + C)) # 식 (3.1), p* [bar]
# 표 3-1의 고온역(379-573 K) 상수를 373 K에 적용
p = water_vapor_pressure(373.0, 3.55959, 643.748, -198.043)
print(round(p, 3)) # 0.759 가 출력된다
✔ 판정: 로그 밑 10을 10 ** (...)로 정확히 옮겼고, 반환 단위가 프롬프트와 일치한다. 코드 자체는 식 (3.1)을 바르게 구현했다. 그런데 출력 0.759 bar가 판정 기준 1.013 bar와 어긋난다. 코드가 아니라 입력을 의심할 차례다.
검증 루틴 3종
- 단위 체크: 입력 T [K], 출력 p* [bar], 로그 밑 10. 표 3-1·식 (3.1)과 모두 일치한다.
- 대조값: 물은 1 atm에서 100 °C에 끓으므로 참값 p* = 1.013 bar. 계산값 0.759 bar는 약 25 % 낮다(오차 (0.759 − 1.013)/1.013 = −25 %).
- 극한값: 373 K는 쓴 상수의 유효범위(379 – 573 K) 밖으로, 아래쪽으로 벗어난 외삽이다. 25 % 오차의 원인이 여기 있다.
분석 코드는 옳았고 틀린 것은 상수 선택이었다. 유효범위가 373 K를 포함하는 상수 벌(표 3-1의 344 – 373 K)로 바꿔 같은 함수를 부르면 약 1.008 bar가 나와 참값과 1 % 이내로 맞는다. 같은 식, 같은 코드인데 상수의 유효범위 하나로 답이 25 %에서 1 %로 바뀐다. 여기서 일반화할 교훈은 둘이다. 첫째, 상관식은 유효범위 안에서만 믿는다. 둘째, 코드가 옳아도 답이 틀릴 수 있으므로 검증은 코드가 아니라 결과를 겨냥한다. What-if: 표 3-1의 두 상수 벌로 각각 473 K(200 °C)의 증기압을 계산해, 이번에는 어느 쪽이 유효범위 안인지 뒤집어 확인해 보라.
예제 3.3-1은 코드가 옳은데 입력이 틀린 경우였다. 이제 코드 자체가 틀리는 두 경우를 본다. 첫째는 물성 날조, 둘째는 단위·로그 밑 오류다. 두 사례 모두 검증 루틴이 없으면 지나쳤을 오류다.
생각해보기
예제 3.3-1의 25 % 외삽 오차와 경고 ②의 log 밑 오류는 둘 다 "값이 참값과 어긋난다"는 증상을 공유한다. 그런데 원인은 하나는 입력(상수 선택), 다른 하나는 코드(로그 밑)에 있다. 대조값 검사만으로는 두 원인을 구별할 수 없다면, 원인을 가려내기 위해 어떤 추가 점검을 하겠는가. 검증 루틴 3종 중 무엇을 먼저 쓸지 근거와 함께 제안하라.
실습 3 도구②: 플래시카드 생성기 (타이머 없음)
이번 주 도구는 지난주 정리기가 만든 위키 노트를 읽어 암기 카드 덱으로 바꾼다. 입력은 위키 노트(.md) 한정이고, 출력은 Anki가 가져올 수 있는 CSV 파일이다(한 줄 = "앞면,뒷면"). 본인 화공양론 시험범위 노트로 실전 덱 한 벌을 만드는 것이 목표다. 타이머는 없고, 결과는 통과/재도전 2단계로만 처리한다.
준비물
- 1주차 개발환경 (PowerShell에서
claude실행 확인, Git Bash 금지) - 실습 3 스타터 repo (노트 읽기·덱 저장 함수는 이미 완성돼 있다)
- 2주차 도구①로 만든 본인 화공양론 위키 노트 3편 이상
- Anki 설치(완성한 CSV를 실제로 가져와 확인한다)
def read_wiki_note(path):
"""위키 노트(.md) 한 편을 문자열로 읽어 온다."""
def save_deck_csv(filename, cards):
"""cards(리스트: (앞면, 뒷면) 튜플)를 Anki용 CSV로 저장한다."""
과제
- 실습 3.1 스타터 repo를 클론하고
python flashcards.py를 실행하라. (성공 시 예제 노트에서 만든 카드 한두 장이 화면에 출력된다) - 실습 3.2 생성기의 일을 §2.1처럼 분해해 README에 분해표(노트 읽기 → 카드 후보 뽑기 → CSV 저장)로 기록하라. (각 행이 함수 하나에 대응해야 한다)
- 실습 3.3 노트 문자열에서 "앞면/뒷면" 쌍을 뽑는 함수를 4요소 프롬프트로 생성하라. 완료 후 커밋하라. (예제 노트에서 카드 후보가 리스트로 나오면 완료)
- 실습 3.4 카드 리스트를
save_deck_csv로 저장하고, Anki에서 그 CSV를 가져오라. (Anki 덱에 카드가 실제로 들어오면 완료) - 실습 3.5 대조 검사. 생성된 카드에 원본 노트에 없는 숫자·상수가 끼어들지 않았는지 한 장씩 대조하라. 물성값이 있으면 NIST WebBook과 맞대어 보라. 날조·불일치가 있으면 카드 원문과 판정 근거를 README에 기록하라. (완료 기준: 판정 결과가 README에 있다)
- 실습 3.6 본인 화공양론 노트 3편으로 실전 덱을 만들어 저장하고 최종 커밋하라. (deck.csv와 검증 기록이 저장소에 있으면 완료)
완성 기준 (통과 / 재도전)
- 생성기 v1이 작동한다: 위키 노트를 넣으면 Anki가 가져올 수 있는 CSV가 나온다
- 본인 화공양론 시험범위로 만든 실전 덱 1벌이 저장소에 있다
- README에 분해표 + 실습 3.5의 대조 검사 판정이 기록돼 있다
- 의미 있는 커밋 3회 이상 (실습 3.3 / 3.4 / 3.6 각 1회 이상)
막혔는가? 힌트 프롬프트 (정답 코드가 아니다)
- 카드가 노트 내용과 어긋날 때: "이 카드의 뒷면 문장이 아래 원본 노트에 실제로 있는지 한 문장씩 대조해 줘. 노트에 없는 주장은 표시해 줘: (노트·카드 붙여넣기)"
- 물성값 진위가 의심될 때: "이 물성값을 내가 NIST WebBook에서 직접 확인하려면 어떤 검색어로 어느 항목을 봐야 하는지 절차만 알려 줘. 값은 네가 말하지 마."
- CSV가 Anki에서 안 열릴 때: "이 오류 메시지의 원인 후보 3개와 각각 확인하는 방법을 알려 줘. 내 환경은 Windows, Anki CSV 가져오기: (오류 전문 붙여넣기)"
퇴실 전 기록
실습 종료 15분은 위키 시간이다. 오늘 만든 것, 막힌 것, 그리고 실습 3.5에서 잡아낸 날조 카드 1건을 원문과 함께 노트로 남겨 커밋해야 퇴실한다. 이 "내가 잡은 환각" 기록이 5주차 화공 도구 검증의 밑천이 된다.
요약
- S 3-1 검증 루틴 3종: ① 단위 체크(입력·출력 단위가 식과 맞는가) → ② 대조값(NIST·문헌의 아는 값 한 점과 비교) → ③ 극한값 sanity(극한·유효범위 경계에서 물리가 말이 되는가). 순서는 단위 → 대조값 → 극한값.
- S 3-2 환각의 세 얼굴: 물성 날조(없는 값) · 단위 오류(맞는 개념, 틀린 단위·로그 밑) · 자신만만한 오답(어조는 정답 여부와 무관). 판정은 어조가 아니라 대조에서 나온다.
- S 3-3 Antoine 식 (3.1) log10 p* = A − B/(T + C). 상수를 받으면 로그 밑·압력 단위·온도 단위·유효범위 4종을 먼저 확인한다.
| 환각 유형 | 이 장의 실제 사례 | 1차로 잡는 루틴 |
|---|---|---|
| 물성 날조 | 날조된 임계온도 350 °C, 가짜 출처 | 대조값 (NIST) |
| 단위 오류 | log10 자리에 exp 사용 → 200 °C에서 3.4 bar | 단위 체크 (로그 밑) |
| 외삽 오차 | 유효범위 밖 상수로 100 °C에서 0.76 bar | 극한값 (유효범위) |
이 장에서 AI 협업 사이클의 마지막 블록 ⑥이 세 개의 고정된 질문(단위·대조값·극한값)으로 형태를 갖췄다. 도구 사다리에서는 정리기(도구①)에 이어 플래시카드 생성기(도구②)가 손에 들어왔고, 두 도구는 같은 위키를 읽고 쓰며 서로 이어진다. 그리고 이 장에서 얻은 것은 도구만이 아니다. 매끄러운 출력 앞에서 멈추고 아는 값 한 점과 맞대어 보는 습관도 함께 얻었다. 이 습관을 5주차에는 화공 도구에, 최종 시험에는 2시간 실기에 그대로 가져간다.
용어 정리
- 환각(hallucination)
- LLM이 근거 없이 그럴듯하게 지어낸 출력. 고장이 아니라 다음 토큰 예측의 부산물이다.
- 물성 날조
- 존재하지 않는 물성 상수·끓는점·임계값을 모델이 자신 있게 지어내는 환각의 한 형태.
- 검증 루틴 3종
- 단위 체크·대조값·극한값 sanity의 세 가지 화공 검증 절차. 15장까지 형식이 불변이다.
- 물성(physical property)
- 물질의 상태를 특징짓는 측정 가능한 양. 끓는점·증기압·임계온도 등.
- 증기압(vapor pressure)
- 액체가 자신의 기체와 평형을 이루는 압력. 온도가 오르면 커진다.
- 외삽(extrapolation)
- 상관식을 유효범위 밖에 적용해 값을 구하는 일. 오차가 급격히 커진다.
- NIST WebBook
- 미국 국립표준기술원이 운영하는 공개 물성 데이터베이스. 대조값의 웹 조회처.
- CoolProp
- 순수 유체의 열역학 물성을 계산하는 오픈소스 라이브러리. 대조값의 코드 조회처(5장에서 사용).
연습문제
Q군: 개념·읽기 (AI 없이 푼다)
- Q3.1 ● 환각의 세 얼굴(물성 날조·단위 오류·자신만만한 오답)을 각각 한 문장으로 정의하고, 검증 루틴 3종 중 어느 것이 1차로 잡는지 짝지어라.
- Q3.2 ● 코드 3-1의 목적과 동작을 한국어 2~3문장으로 설명하라.
10 ** (...)가math.exp가 아니어야 하는 이유를 반드시 포함하라. - Q3.3 ●● AI가 낸 다음 카드를 채점하고 교정하라. 어느 검증 루틴이 이 오류를 잡는지 밝히고, 판정에 쓸 대조값을 하나 제시하라.
앞면: 물은 몇 도에서 끓는가(1 atm)? 뒷면: 물은 1 atm에서 90 °C에 끓는다.
P군: 제작 (AI 사용 전제)
- P3.1 ● 예제 3.3-1의 함수로, 표 3-1의 두 상수 벌을 써서 473 K(200 °C)의 증기압을 각각 계산하라. 참값 약 15.5 bar와 대조하고, 어느 상수 벌이 유효범위 안인지 극한값 검사로 판정한 뒤 검증 3종을 README에 기록하라.
- P3.2 ●● (메타인지) 플래시카드 생성기가 "그럴듯하지만 틀린 카드"를 내놓게 만드는 프롬프트를 두 가지 설계하라. 각각 어떤 환각 유형이 나올지 예측하고, 실제로 재생성해 예측과 비교한 뒤, 그 카드가 검증 루틴 3종 중 무엇에 걸리는지 밝혀라.
- P3.3 ●●● 플래시카드 생성기에 "물성값 자동 플래그" 기능을 추가하라. 카드 뒷면에 숫자+단위 형태의 물성값이 있는데 그 값이 원본 노트에 없으면
[검증필요]표시를 붙이는 함수를 분해표에 추가하고, 4요소 프롬프트로 생성한 뒤, 무엇을 근거로 "물성값"을 판정했는지 README에 적어라. 테스트 2개 이상을 포함하라.
위키 기록 과제 (이번 주 의미 커밋 ≥1이 채점 지표다)
- 이 장의 핵심(검증 루틴 3종 + 환각 세 얼굴)을 1페이지로 정리하라(
#seed태그). - 실습 3.5에서 잡아낸 "내가 잡은 환각" 1건을 카드 원문·대조값과 함께 기록하라.
- 타 과목 연결 1건: 검증 루틴 3종 중 하나가 화공양론·일반화학의 어느 계산에서 이미 쓰이고 있는지 한 문단으로 적어라.
4장 기억의 과학과 나의 학습 OS
이번 주의 도구: 도구③ 스터디 플래너, 그리고 학습 OS의 첫 통합
다음 상황을 생각해 보자. 화공양론 중간고사를 두 주 앞둔 학생이 3주 전 강의노트를 펼친다. 그날 분명히 이해하고 넘어간 습도 계산인데, 지금 보니 처음 보는 내용처럼 낯설다. 불안해진 학생은 노트를 처음부터 다시 읽기 시작한다. 두 시간 뒤 남는 것은 "읽었다"는 안도감뿐이다. 노트를 덮고 문제를 풀어 보면 손이 멈춘다.
이 학생에게 부족한 것은 노력도 자료도 아니다. 2주차에 만든 도구① 강의노트 정리기는 수업 내용을 위키에 쌓아 왔고, 3주차의 도구② 플래시카드 생성기는 그 노트를 질문 카드로 바꿔 놓았다. 없는 것은 하나, "오늘 무엇을 복습해야 하는가"에 답해 주는 시스템이다.
이번 장이 그 마지막 조각을 채운다. 앞 절반에서는 뇌가 기억을 다루는 방식, 즉 왜 다시 읽기는 잘 남지 않고 꺼내 보기는 남는가를 다루고, 뒤 절반에서는 그 원리를 파일과 JSON이라는 기술로 옮긴다. 도구 셋이 파일 규약으로 맞물리는 순간, 흩어져 있던 산출물이 처음으로 하나의 학습 OS로 돌아가기 시작한다.
이 장에서 다루는 것
- 간격반복·인출연습·시험 효과의 원리를 과장 없이 정리한다
- 위키 노트의 성장단계 태그 #seed → #growing → #evergreen과 승격 기준을 정한다
- 파일 입출력과 JSON의 최소 문법을 익힌다. 도구가 꺼져도 기억을 잃지 않게 만드는 기술이다
- 도구③ 스터디 플래너를 만들고, 도구①→wiki→도구②→도구③ 파이프라인을 처음으로 통합한다
학습목표 이 장을 마치면 다음을 할 수 있다.
- 다시 읽기보다 인출연습이 장기 기억에 유리한 이유를 시험 효과로 설명할 수 있다.
- 복습 간격 규칙을 코드로 옮겨 임의의 노트의 다음 복습일을 계산할 수 있다.
- 위키 노트를 #seed·#growing·#evergreen 3단계로 분류하고 승격 기준을 적용할 수 있다.
- 파이썬으로 텍스트 파일과 JSON 파일을 읽고 쓸 수 있다.
- 도구①~③을 파일 규약으로 연결한 파이프라인을 구성하고, 이음매를 테스트로 검증할 수 있다.
이번 주 실습에서는 §4.3의 파일·JSON 문법과 §4.4의 파이프라인 설계를 사용해 도구③ 스터디 플래너를 만들고, 도구①·②와 처음으로 연결한다. 여러 파일이 규약으로 맞물려 저장소 전체가 하나로 작동해야 하는 이 형식은, 최종 시험(2시간 실기)이 요구하는 결과물의 축소판이다.
4.1 잊는 뇌, 꺼내는 뇌
시험을 망친 뒤 가장 흔한 진단은 "덜 읽어서"다. 그래서 다음 시험에는 더 여러 번 읽는다. 교육심리학의 진단은 방향이 반대다. 문제는 덜 읽은 것이 아니라 덜 꺼낸 것이다.
4.1.1 시험 효과: 꺼내는 행위가 기억을 만든다
기억에서 정보를 스스로 재구성해 꺼내는 행위를 인출(retrieval)이라 한다. 다시 읽기는 정보를 눈앞에 가져다 놓는 일이고, 인출은 눈앞에 없는 것을 머리에서 복원하는 일이다. 같은 내용을 다뤄도 뇌가 하는 일이 다르다.
인출 행위 자체가 그 기억을 강화하는 현상을 시험 효과(testing effect)라 하고, 시험 효과를 의도적으로 학습에 쓰는 전략을 인출연습(retrieval practice)이라 한다. 같은 시간을 쓴다면 다시 읽기보다 스스로 꺼내 보는 쪽이 장기 기억에 남는다는 것이 시험 효과 연구들의 공통 결론이다. 시험은 실력을 재는 장치이면서, 실력을 만드는 장치이기도 하다.
다시 읽기가 기대만큼 남지 않는 이유도 여기서 나온다. 세 번째 읽는 노트는 술술 넘어간다. 그 매끄러움은 "노트가 눈에 익었다"는 신호이지 "시험장에서 백지 위에 재구성할 수 있다"는 신호가 아니다. 익숙함을 이해로 착각하는 순간, 공부 시간과 성적이 따로 놀기 시작한다.
구체적인 장면으로 옮겨 보자. 3주차에 만든 플래시카드 한 장의 앞면에 "절대습도와 상대습도의 정의 차이는?"이 적혀 있다. 뒷면을 보기 전에 멈춰서 답을 말해 보는 그 몇 초에 학습이 일어난다. 답이 틀렸어도 좋다. 인출을 시도한 뒤에 본 정답은 그냥 읽은 정답보다 오래 남는다. 도구② 플래시카드 생성기는 처음부터 인출연습 장치로 설계된 것이다.
4.1.2 간격반복: '언제'의 설계
인출연습이 "어떻게"의 답이라면, 남은 질문은 "언제"다. 복습 사이의 간격을 점점 늘려 가며 인출을 반복하는 전략을 간격반복(spaced repetition)이라 한다.
논리는 이렇다. 방금 본 것을 다시 꺼내는 일은 쉽고, 쉬운 인출은 기억을 거의 바꾸지 못한다. 조금 잊힐 때까지 기다렸다가 꺼내면 인출은 힘들어지지만, 성공했을 때 기억이 훨씬 단단해진다. 학습에 오히려 유익한 이런 어려움을 바람직한 어려움(desirable difficulty)이라 한다. 몰아서 열 번 읽는 벼락치기가 시험 다음 날 증발하는 이유가, 뒤집으면 간격반복이 작동하는 이유다.
그렇다면 간격은 정확히 며칠이어야 하는가. 최적값은 자료의 난도와 사람에 따라 달라서, 하나의 정답 수열은 없다. 이 책은 출발 규칙으로 1일 → 3일 → 7일 → 14일을 채택한다. 복습에 성공할 때마다 다음 간격이 두 배 안팎으로 늘어난다는 구조가 본질이고, 수열의 값 자체는 조정 대상이다. 그래서 4.3절의 예제는 이 수열을 코드에 박아 넣지 않고 데이터로 분리한다. 나중에 본인 기록을 보고 값을 바꾸더라도 코드는 건드리지 않게 하기 위해서다.
한 가지는 분명히 해 두자. 간격반복은 이해를 대체하지 않는다. 이해한 것이 새 나가지 않게 막는 유지 장치로 보면 된다. 이해는 수업과 문제 풀이의 몫이고, 이 장의 도구는 그 뒤를 맡는다.
스스로 점검
- 노트가 "술술 읽힌다"는 느낌이 시험 준비 상태의 증거가 되지 못하는 이유는 무엇인가?
- 같은 카드를 하루에 10번 복습하는 것과 열흘에 걸쳐 3번 복습하는 것 중 간격반복 관점에서 어느 쪽이 유리하며, 그 근거는 무엇인가?
생각해보기 복습 시간이 하루 20분으로 고정되어 있다고 하자. 매일 덱 전체를 훑는 전략과, 간격 규칙이 골라 준 일부 카드만 깊게 인출하는 전략이 경쟁한다. "복습에서 빠지는 카드가 생기는 위험"과 "카드 한 장당 인출 난도"를 근거로 두 전략을 각각 방어해 보라. 어느 쪽 논거가 더 튼튼한가?
4.2 위키 구조화: 씨앗에서 상록수까지
인출연습은 카드에만 적용되는 원리가 아니다. 1주차부터 운영해 온 위키 노트에도 같은 원리를 심을 수 있다. 핵심 장치는 노트의 성장단계를 표시하는 세 개의 태그 #seed → #growing → #evergreen이다. 노트를 완성된 문서가 아니라 자라는 생물로 취급하는 이 관례를 이 수업의 위키 규약으로 채택한다.
| 태그 | 상태 | 승격 기준 (검증 가능한 형태) |
|---|---|---|
| #seed | 수업 당일 던져 넣은 날것의 메모 | 존재 자체. 한 줄 요약·에러 스크린샷·미완성 문장 전부 허용 |
| #growing | 내 언어로 소화된 노트 | 원문을 덮고 내 말로 다시 쓴 설명 + 예제나 실행 결과 1개 + 다른 노트로 가는 링크 1개 이상 |
| #evergreen | 재사용되는 지식 | 노트 2개 이상(타 과목 포함)과 연결 + 실습·시험에서 실제 인용 + "내가 틀렸던 것" 반영 |
승격 기준을 이렇게 박아 두는 이유는 승격 작업 자체가 인출연습이기 때문이다. #seed를 #growing으로 올리려면 원문을 덮고 내 말로 다시 써야 하고, 그것이 곧 인출이다. #growing을 #evergreen으로 올리려면 다른 노트·다른 과목과 연결해야 하는데, 연결은 나중에 그 지식을 꺼낼 인출 단서를 늘린다. 태그는 "이 노트에 어떤 학습 행위가 일어났는가"의 기록으로 쓴다.
채점과의 연결도 명확하다. 위키 채점 기준(마일스톤 3회, 주당 의미 있는 커밋)은 1장에 정리해 두었다. 마일스톤마다 evergreen 승격 수와 링크 밀도가 집계되며, 첫 집계인 마일스톤①이 바로 다음 주다.
도구가 위키를 읽으려면 사람만 알아보는 노트로는 부족하다. 노트 머리에 기계가 읽을 수 있는 형식으로 메타데이터를 적는 관례를 프런트매터(frontmatter)라 한다. 이 수업의 최소 규약은 세 필드다.
---
title: 이상기체
tags: [seed]
updated: 2026-09-27
---
# 이상기체
오늘 화공양론: PV = nRT가 잘 맞는 조건이 따로 있다. (내일 내 말로 다시 쓸 것)
구획선 --- 두 줄 사이가 프런트매터다. tags는 성장단계, updated는 마지막 수정일이다. 이 세 줄이 4.4절에서 도구들을 잇는 접착제가 된다.
스스로 점검
- #growing 승격 기준에 "다른 노트로 가는 링크 1개 이상"이 들어 있는 이유는 무엇인가?
- 어제 수업 노트 하나를 골라 보라. 표 4-1 기준으로 지금 어느 단계이며, 다음 단계로 올리려면 정확히 무엇을 추가해야 하는가?
4.3 파일 입출력과 JSON: 도구에 기억을 심는 법
지금까지 만든 도구에는 공통된 한계가 있다. 실행이 끝나면 전부 잊는다는 것이다. 변수에 담긴 값은 프로그램이 종료되는 순간 사라진다. 스터디 플래너는 "이 노트를 마지막으로 언제 복습했는가"를 다음 실행 때도 기억해야 하므로, 데이터를 프로그램 바깥에 남기는 수단이 필요하다. 프로그램이 꺼져도 데이터가 남는 성질을 영속성(persistence)이라 하고, 그 가장 기본적인 수단이 파일이다.
4.3.1 텍스트 파일 읽고 쓰기
파일을 열 때 받는 것은 파일 내용 자체가 아니라 파일로 통하는 손잡이다. 이 손잡이를 파일 핸들(file handle)이라 한다. 핸들을 통해 한 줄씩 읽고, 다 쓰면 닫는다. 최소 문형부터 보자.
fhand = open('wiki/이상기체.md', 'r', encoding='utf-8') # 'r' = 읽기 모드
count = 0
for line in fhand: # 파일 핸들을 순회하면 한 줄씩 나온다
count = count + 1
fhand.close() # 다 썼으면 닫는다
print('줄 수:', count)
encoding='utf-8'은 이 책의 전 코드 공통 규약이다. 윈도우의 기본 인코딩은 UTF-8이 아닐 수 있어서, 이 인자를 빼면 한글 노트가 깨져 읽히는 사고가 실습실에서 자주 일어난다. AI가 생성한 코드에 이 인자가 빠져 있으면 추가를 요구하라.
없는 파일을 읽기 모드로 열면 어떻게 되는가. 직접 겪어 보는 편이 빠르다.
>>> open('없는파일.md', 'r', encoding='utf-8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '없는파일.md'
프로그램이 그 자리에서 멈춘다. 에러 이름 FileNotFoundError를 기억해 두자. 실습에서 plan.json이 아직 없을 때 정확히 이 에러를 만나게 되고, 그때 "파일이 없으면 빈 계획으로 시작하라"는 예외 처리를 AI에게 요구하게 된다.
쓰기는 모드만 바꾸면 된다. 다만 쓰기 모드에는 함정이 있다.
fout = open('오늘의_복습.md', 'w', encoding='utf-8') # 'w' = 새로 쓰기
fout.write('# 2026-09-28 복습 목록\n') # \n = 줄바꿈 문자
fout.write('- 이상기체\n')
fout.close() # 닫아야 마지막 내용까지 디스크에 기록된다
'w' 모드는 파일이 이미 있으면 기존 내용을 전부 지우고 새로 시작한다. 매번 새로 만드는 "오늘의 복습 목록"에는 적합하지만, 쌓아 가야 하는 데이터에 쓰면 기존 내용을 잃는다. 끝에 덧붙이는 모드는 'a'다. 이 구분이 얼마나 중요한지는 아래 사례가 보여 준다.
4.3.2 JSON: 구조가 있는 기억
복습 이력은 줄글이 아니라 구조다. 노트마다 마지막 복습일과 진행 단계가 붙어 있어야 한다. 이런 데이터를 담는 파이썬 자료형이 딕셔너리(dictionary)다. 키(key)를 넣으면 값(value)이 나오는 자료형으로, plan['이상기체']처럼 대괄호 안에 키를 써서 값을 꺼낸다. 문법 해부는 5장의 몫이고, 지금은 읽을 수 있으면 충분하다.
딕셔너리와 리스트를 중첩해 만든 구조를 텍스트로 저장하고 교환하는 표준 형식을 JSON(JavaScript Object Notation)이라 한다. 이름과 달리 자바스크립트 전용이 아니며, 오늘날 프로그램 사이의 데이터 교환에서 사실상 표준으로 쓰인다. 파이썬 표준 라이브러리 json이 딕셔너리를 파일로, 파일을 다시 딕셔너리로 왕복시켜 준다.
import json
plan = {
'이상기체': {'last': '2026-09-27', 'stage': 0}, # 날짜 형식: 'YYYY-MM-DD'
'습도 계산': {'last': '2026-09-21', 'stage': 2}, # stage: 0부터 시작하는 정수
}
with open('plan.json', 'w', encoding='utf-8') as f:
json.dump(plan, f, ensure_ascii=False, indent=2) # 사람도 읽을 수 있게 저장
with open('plan.json', encoding='utf-8') as f:
plan2 = json.load(f) # 파일 → 딕셔너리 복원
print(plan2['습도 계산']['stage']) # 2
새 문형이 하나 등장했다. with open(...) as f: 블록은 블록이 끝나는 순간 파일을 자동으로 닫는다. close()를 잊는 실수를 구조적으로 차단하므로, 이후 모든 코드에서 이 문형을 기본으로 쓴다. ensure_ascii=False는 한글을 사람이 읽을 수 있는 그대로 저장하게 하는 옵션이다.
생성 코드에서 자주 마주치는 함정이 하나 있다. json.load에는 파일 핸들이 들어가야 하는데, 경로 문자열을 바로 넘기는 코드가 종종 생성된다. 결과는 이렇다.
>>> json.load('plan.json')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
...
AttributeError: 'str' object has no attribute 'read'
에러 문구가 원인을 말해 주지 않는 전형적인 경우다. "문자열에는 read가 없다"는 말의 진짜 뜻은 "파일 핸들 자리에 문자열이 들어왔다"이다. 이 에러를 만나면 open()을 거쳤는지부터 확인하라.
예제 4.3-1 다음 복습일 계산기
문제 코드 4-4 형식의 plan.json을 읽어, 간격 규칙 1·3·7·14일에 따라 각 노트의 다음 복습일을 계산하고, 오늘(2026-09-28) 복습해야 할 노트 목록을 출력하는 프로그램을 만들어라. 단계가 간격 개수를 넘어선 노트는 마지막 간격(14일)을 계속 쓴다.
풀이 전략 문제를 셋으로 분해한다. ① JSON 읽기는 코드 4-4의 문형을 그대로 재사용하므로 직접 쓴다. ② 날짜 계산과 오늘 목록 필터는 표준 라이브러리 datetime이 필요하므로 AI에게 함수 단위로 맡긴다. ③ 검증, 즉 달력 손 대조와 극한값 주입은 직접 한다. 프롬프트에는 데이터 형식(날짜 문자열, stage 정수)과 경계 조건(단계 초과)을 명시한다.
plan.json에 노트별로 last(마지막 복습일, 'YYYY-MM-DD' 문자열)와 stage(0부터 시작하는 정수)가 있어. 복습 간격 [1, 3, 7, 14]일 규칙으로 다음 복습일을 계산하는 함수 next_review(last, stage)와, 오늘 날짜를 받아 복습할 노트 이름 목록을 돌려주는 함수 due_today(plan, today)를 만들어 줘. 표준 라이브러리만 쓰고, stage가 간격 개수를 넘으면 마지막 간격을 계속 쓰게 하고, 주석은 한국어로 달아 줘.
from datetime import date, timedelta
INTERVALS = [1, 3, 7, 14] # 복습 간격 (일) — 규칙은 데이터로 분리
def next_review(last, stage):
"""마지막 복습일(ISO 문자열)과 단계로 다음 복습일을 계산한다."""
last_day = date.fromisoformat(last) # 'YYYY-MM-DD' → date 객체
idx = min(stage, len(INTERVALS) - 1) # 단계 초과 시 마지막 간격 유지
return last_day + timedelta(days=INTERVALS[idx])
def due_today(plan, today):
"""오늘 복습해야 할 노트 이름 목록을 돌려준다."""
due = []
for name in plan: # 딕셔너리 순회 = 키 순회
item = plan[name]
if next_review(item['last'], item['stage']) <= today:
due.append(name) # 예정일이 지난 노트도 포함
return due
검증 코멘트: next_review가 문자열이 아닌 date 객체를 돌려주므로 비교(<=)가 날짜끼리 이루어진다. 문자열 비교였다면 형식이 다를 때 조용히 틀렸을 것이다. 단계 초과를 min으로 막은 것 확인. 부등호가 <=라서 예정일이 "지난" 노트(밀린 복습)도 목록에 포함된다(의도와 일치).
실행해 보자.
>>> with open('plan.json', encoding='utf-8') as f:
... plan = json.load(f)
...
>>> due_today(plan, date(2026, 9, 28))
['이상기체', '습도 계산']
'이상기체'는 9월 27일에 0단계였으니 1일 뒤인 28일이 예정일이고, '습도 계산'은 21일에 2단계였으니 7일 뒤인 28일이 예정일이다. 둘 다 오늘 목록에 들어온 것이 맞다.
- 형식 체크: 입력은 'YYYY-MM-DD' 문자열, 내부 계산은 date 객체, 간격은 일 단위(
timedelta(days=...))다. 형식 혼용이 없다. - 대조값: 달력 손 계산으로 9월 21일의 7일 뒤는 9월 28일이다. 함수 출력과 일치한다.
- 극한값: stage=99를 넣어도 IndexError 없이 마지막 간격 14일이 적용되고, 빈 계획
{}에는 빈 목록[]가 반환된다. 실행으로 확인했다.
분석 배울 것은 두 가지다. 첫째, 규칙(INTERVALS)을 데이터로 분리하면 간격 조정이 코드 수정 없이 이루어진다. 4.1절에서 "수열의 값은 조정 대상"이라 한 말의 코드 번역이다. 둘째, 생성 코드에서 가장 먼저 의심할 곳은 경계 조건이다. 부등호 하나(<였다면 밀린 복습이 영영 목록에 안 뜬다)와 단계 초과 처리(min이 없었다면 크래시)가 도구의 동작을 좌우했다. What-if: INTERVALS를 [1, 2, 4, 8]로 바꾸면 due 목록이 어떻게 달라질지 예측한 뒤, 실행으로 확인해 보라.
스스로 점검
- 코드 4-3에서
'w'를'a'로 바꾸고 두 번 실행하면 파일 내용은 어떻게 달라지는가? json.dump와json.load의 인자에 공통으로 들어가는 것은 무엇이며, 그 자리에 경로 문자열을 넣으면 어떤 에러가 나는가?
4.4 학습 OS: 도구를 파이프라인으로 잇는다
도구가 셋이 되었다. 이제 이들을 하나의 시스템으로 묶는다. 한 도구의 출력 파일이 다음 도구의 입력이 되도록 연결한 사슬을 파이프라인(pipeline)이라 한다.
강의노트(텍스트 붙여넣기)
│ 도구① 강의노트 정리기
▼
wiki/*.md (프런트매터: title, tags, updated)
│ │
│ 도구② 플래시카드 생성기 │ 도구③ 스터디 플래너 ←── plan.json (복습 이력)
▼ ▼
Anki 덱 오늘의_복습.md
이 파이프라인과 위키, 그리고 그것을 매일 굴리는 복습 습관 전체를 이 수업에서는 학습 OS라 부른다. 컴퓨터의 운영체제가 프로그램에게 저장과 메모리를 제공하듯, 학습 OS는 공부에게 저장(위키)·인출(플래시카드)·스케줄(플래너)을 제공한다. 시스템의 요점은 순환이다. 도구를 쓰면 위키가 자라고, 위키가 자라면 카드와 복습 목록이 저절로 늘어난다.
통합의 실체는 규약이다. 폴더 구조, 파일 이름, 프런트매터 필드, plan.json의 스키마가 도구 사이의 인터페이스다. 도구①이 tags 필드를 빼먹으면 도구③이 위키를 읽지 못한다. 그래서 규약은 README에 명문화하고, AI에게 연결 코드를 시킬 때마다 그 규약을 프롬프트에 그대로 붙여넣는다. 이것이 파이프라인 작업의 표준 프롬프트 패턴이다(요약 S 4-4).
- 문제 정의
- 분해
- 프롬프트
- 생성 코드 읽기
- 테스트
- 화공 검증
파이프라인의 고장은 각 도구 안이 아니라 이음매에서 난다. 도구 셋이 각자 완벽히 돌아가도, 도구①이 쓰는 날짜 형식과 도구③이 기대하는 형식이 다르면 시스템 전체가 멈춘다. 그래서 통합 작업의 마지막은 언제나 이음매 검사다.
예제 4.4-1 파이프라인 이음매 검사기
문제 위키 폴더의 노트 목록과 plan.json의 등록 목록을 비교해, ① 위키에는 있는데 계획에 없는 노트(복습 누락 위험)와 ② 계획에는 있는데 위키에서 사라진 노트(유령 항목)를 찾아 보고하는 검사 스크립트를 만들어라.
풀이 전략 폴더에서 파일 목록을 얻는 부분은 pathlib이 필요하므로 AI에게 맡기되, 비교 논리는 지금 읽을 수 있는 for와 if로 풀어 달라고 프롬프트에 명시한다. 검증은 정상 상태가 아니라 일부러 고장 낸 상태에서 한다. 검사기가 실제로 고장을 잡는지 봐야 하기 때문이다.
위키 폴더의 .md 파일 이름 목록(확장자 제외)과 plan.json의 키 목록을 비교해서, 한쪽에만 있는 이름을 각각 리스트로 돌려주는 함수를 만들어 줘. 표준 라이브러리만 쓰고, 비교 부분은 set 말고 for와 if로 풀어 써 줘. 주석은 한국어로.
import json
from pathlib import Path
def check_seams(wiki_dir, plan_file):
"""wiki 노트와 복습 계획의 이음매를 검사한다."""
with open(plan_file, encoding='utf-8') as f:
plan = json.load(f)
notes = []
for p in Path(wiki_dir).glob('*.md'): # wiki 폴더의 모든 노트
notes.append(p.stem) # p.stem = 확장자를 뺀 이름
missing = [] # wiki에 있는데 계획에 없음
for name in notes:
if name not in plan:
missing.append(name)
ghost = [] # 계획에 있는데 wiki에 없음
for name in plan:
if name not in notes:
ghost.append(name)
return missing, ghost
검증 코멘트: 일부러 위키에 새 노트 증류.md를 만들고 실행하자 missing에 '증류'가 잡혔다. 이번에는 plan.json은 그대로 두고 노트 파일 하나를 지우자 ghost에 잡혔다. 고장을 심어야 검사기가 살아 있음을 확인할 수 있다. 비교 대상의 형식도 확인했다. 파일 쪽은 p.stem(확장자 제거), plan 쪽은 키이므로 '이상기체.md'와 '이상기체'를 비교하는 불일치가 없다.
- 형식 체크: 양쪽 다 "확장자 없는 노트 이름"으로 통일해 비교한다.
p.stem이 이를 보장한다. - 대조값: 노트 3개짜리 미니 위키로 손으로 센 결과(누락 1건, 유령 0건)와 함수 출력이 일치한다.
- 극한값: 빈 위키 폴더 → (빈 목록, 전체 유령), 빈 plan.json → (전체 누락, 빈 목록). 둘 다 크래시 없이 반환된다.
분석 통합 테스트의 본체는 "고장을 심고 검출되는지 확인"이다. 검사기가 아무것도 잡지 못했다는 결과는 파이프라인이 건강하다는 뜻일 수도, 검사기 자신이 작동하지 않는다는 뜻일 수도 있다. 후자를 배제하려면 고장을 직접 심어 보아야 한다. What-if: 프런트매터의 updated가 plan의 last보다 최신인 노트(복습 후 수정된 노트)를 찾는 세 번째 검사를 AI로 추가 생성해 보라.
생각해보기 학습 OS의 어느 고리가 끊겼을 때 시스템 전체가 죽는가? 그림 4-1에서 단일 장애점이라 생각하는 지점을 하나 짚고, 그 고리가 끊겼음을 사람이 아니라 코드가 알아채게 만드는 방법을 두 가지 제안하라.
실습 4: 도구③ 스터디 플래너 + 학습 OS 미니 통합
예상 소요 90분 + 퇴실 전 위키 15분. 타이머는 없다. 시간제한과 공개 지목 구술은 5주차부터 시작된다. 이번 주 결과는 통과/재도전 2단계로만 처리하며, 재도전은 다음 실습 전까지 허용된다.
준비물 도구① 강의노트 정리기 저장소, 도구② 플래시카드 생성기 저장소, 위키 저장소 최신 상태(git pull), 파이썬 실행 확인(python --version). 이번 실습은 새 코드를 처음부터 짜는 것이 아니라 이전 도구 두 개를 재사용하는 것이 절반이다.
- 실습 4.1 데이터 규약을 확정하라. 프런트매터 필드(코드 4-1)와 plan.json 스키마(코드 4-4)를 플래너 저장소 README에 표로 기록하라. (완료 확인: README에 필드 이름·형식·쓰는 도구/읽는 도구가 명시된 표가 있다) 커밋하라.
- 실습 4.2 플래너 코어를 생성하라. 예제 4.3-1의 프롬프트를 출발점으로
next_review·due_today를 만들고, 오늘의 복습 목록을 오늘의_복습.md 파일로 저장하는 기능까지 붙여라. (완료 확인: 실행 한 번으로 오늘의_복습.md가 생성된다) 커밋하라. - 실습 4.3 위키 읽기를 연결하라. 플래너가 위키 폴더의 프런트매터를 읽어, plan.json에 없는 새 노트를 stage 0으로 자동 등록하게 하라. plan.json이 아직 없을 때 FileNotFoundError로 죽지 않고 빈 계획으로 시작하는 처리를 포함하라. (완료 확인: 새 노트 1개를 만들고 실행하면 plan.json에 항목이 늘어난다) 커밋하라.
- 실습 4.4 파이프라인 데모를 실행하라. 이번 주 강의노트 1편을 도구①로 정리하고, 위키에 커밋하고, 도구②로 덱을 만들고, 도구③이 그 노트를 오늘의 복습 목록에 올리는 전 과정을 순서대로 실행한 뒤, 각 단계의 산출물 경로를 README에 기록하라. (완료 확인: README의 데모 기록 4단계가 실제 파일과 일치한다)
- 실습 4.5 이음매를 검사하라. 예제 4.4-1의 검사기를 본인 위키에 돌리고, 불일치가 나오면 원인을 고쳐 누락 0건·유령 0건을 만들어라. 검증 3종(형식·대조값·극한값) 수행 내용을 README에 기록하라. 커밋하라.
- 실습 4.6 페어에게 1분 설명하라. 그림 4-1을 손으로 그려 가며 "내 학습 OS에서 파일이 어떻게 흐르는지"를 설명하라. (완료 확인: 페어가 plan.json의 역할을 되물었을 때 답할 수 있다)
완성 기준 아래를 모두 만족하면 통과다.
- 플래너 실행 한 번으로 오늘의 복습 목록이 파일로 생성된다
- 플래너를 두 번 연속 실행해도 plan.json의 기존 항목이 사라지지 않는다 (실행 전 사본과 항목 수 비교로 증명)
- 새 위키 노트가 자동으로 plan.json에 등록된다
- 도구①→wiki→도구②→도구③ 데모 기록이 README에 있다
- 검증 3종 기록이 README에 있다
- 의미 있는 커밋 3건 이상
막혔는가?
- 에러가 나면: 에러 메시지 전문을 그대로 붙여넣고 "이 에러의 원인 후보 3개와 각각의 확인 방법을 알려 줘"라고 물어라. 요약해서 옮기지 마라. 전문에 단서가 있다.
- 프런트매터 파싱이 막히면: "YAML 라이브러리 없이, --- 구획선 사이의 key: value 줄만 읽는 최소 파서를 만들어 줘. 외부 라이브러리는 쓰지 마." 라고 요구하라.
- plan.json이 자꾸 초기화되면: "이 코드가 파일을 여는 모드를 전부 표로 정리하고, 기존 데이터를 보존하려면 어느 줄을 어떻게 바꿔야 하는지 설명해 줘."라고 물어라. 표 4-2와 대조하라.
퇴실 전 위키 기록(15분) 오늘 만든 것·막힌 것·틀린 것 중 1건을 위키에 커밋해야 퇴실이다. 오늘의 유력 후보는 'w'와 'a'를 헷갈려 잃을 뻔한(또는 잃은) 데이터 이야기다. 에러나 사고 화면을 그대로 붙여라.
요약
- S 4-1 (규칙의 데이터 분리)
INTERVALS = [1, 3, 7, 14]. 정책은 코드가 아니라 데이터에 둔다. 값이 바뀌어도 코드는 그대로다. - S 4-2 (파일 열기 표준 문형)
with open(경로, 모드, encoding='utf-8') as f:. 인코딩을 명시하고 파일을 자동으로 닫는다. - S 4-3 (JSON 왕복) 저장
json.dump(자료, f, ensure_ascii=False, indent=2)/ 복원자료 = json.load(f). 인자는 둘 다 파일 핸들이다. - S 4-4 (규약 프롬프트 패턴) "아래 데이터 규약을 그대로 지켜라: (README의 규약 붙여넣기)". 파이프라인 연결 코드를 시킬 때의 고정 서두다.
| 모드 | 동작 | 파일이 이미 있으면 | 파일이 없으면 | 플래너에서 쓰는 곳 |
|---|---|---|---|---|
'r' | 읽기 | 그대로 읽는다 | FileNotFoundError | 위키 노트·plan.json 읽기 |
'w' | 새로 쓰기 | 내용을 지우고 새로 쓴다 | 새로 만든다 | 오늘의_복습.md 생성 |
'a' | 덧붙이기 | 끝에 이어 쓴다 | 새로 만든다 | 복습 로그 누적 |
이 장으로 Phase 1이 끝났다. AI 협업 사이클에서는 ② 분해(한 문제를 도구 경계로 나누기)와 ⑤ 테스트(이음매에 고장을 심어 검사하기)가 강해졌고, 도구 사다리는 9개 중 3개가 찼다. 무엇보다 도구들이 처음으로 서로를 읽기 시작했다. 이제 위키에 쌓는 모든 노트는 카드가 되고 복습 일정이 된다. 다음 주부터는 이 학습 OS를 밑에 깔고, 화공 도구의 사다리를 오르기 시작한다.
용어 정리
- 인출(retrieval)
- 기억에서 정보를 스스로 재구성해 꺼내는 행위. 다시 읽기와 구별된다.
- 시험 효과(testing effect)
- 인출 행위 자체가 그 기억을 강화하는 현상.
- 인출연습(retrieval practice)
- 시험 효과를 의도적으로 학습 전략으로 쓰는 것. 플래시카드가 대표 장치다.
- 간격반복(spaced repetition)
- 복습 사이의 간격을 점점 늘려 가며 인출을 반복하는 학습 전략.
- 바람직한 어려움(desirable difficulty)
- 인출을 힘들게 하는 대신 성공 시 기억을 더 강화하는, 학습에 유익한 어려움.
- 영속성(persistence)
- 프로그램이 종료되어도 데이터가 남는 성질. 파일이 기본 수단이다.
- 파일 핸들(file handle)
- 열린 파일로 통하는 손잡이 객체. 내용 자체가 아니라 읽고 쓰는 통로다.
- 딕셔너리(dictionary)
- 키를 넣으면 값이 나오는 파이썬 자료형. 키-값 쌍의 모음.
- JSON(JavaScript Object Notation)
- 딕셔너리·리스트의 중첩 구조를 텍스트로 저장·교환하는 표준 형식.
- 프런트매터(frontmatter)
- 노트 머리의 구획선 사이에 기계가 읽을 수 있는 메타데이터를 적는 관례.
- 파이프라인(pipeline)
- 한 도구의 출력 파일이 다음 도구의 입력이 되도록 연결한 사슬.
- 학습 OS
- 도구 파이프라인 + 위키 + 복습 습관 전체를 가리키는 이 수업의 용어. 공부에 저장·인출·스케줄을 제공한다.
연습문제
Q군: 개념·읽기 (AI 없이 풀 것: 시험1 대비)
- Q4.1 다시 읽기 중심의 시험 공부가 시험장에서 실패하는 이유를 인출연습 관점에서 3문장 이내로 설명하라.
- Q4.2 코드 4-2를 실행할 때 wiki/이상기체.md 파일이 없다면 무슨 일이 벌어지는가? 발생하는 에러의 이름까지 정확히 쓰라.
- Q4.3 (EiPE) 코드 4-5의 두 함수가 하는 일을 한국어 2~3문장으로 설명하라. 그리고
min(stage, len(INTERVALS) - 1)이 없다면 어떤 입력에서 어떤 에러가 나는지 밝혀라.
P군: 제작·계산 (AI 사용 전제: 시험2·최종 시험 대비)
- P4.1 과목마다 다른 복습 간격을 쓰도록 플래너를 확장하라. 간격 수열을 코드가 아니라 별도 JSON 설정 파일에 두고, 검증 3종을 README에 기록하라.
- P4.2 "복습 완료" 기능을 추가하라: 오늘 복습을 마친 노트의 stage를 1 올리고 last를 오늘로 갱신한다. 두 번 연속 실행해도 이력이 사라지지 않음을 실행 전후 파일 비교로 증명하라.
- P4.3 (메타인지) 페어의 플래너에 심을 수 있는 "조용한 데이터 손상 버그"를 두 가지 설계하고, 각각이 검증 루틴 3종 중 무엇에 걸리는지 표로 정리하라. 실제로 서로 교환해 심고, 상대의 버그를 잡아내라.
위키 기록 과제 이번 주 필수 커밋 3항: ① 이 장의 핵심을 1페이지로 정리한 노트(#seed로 시작하되, 표 4-1 기준으로 #growing까지 올려 보라) ② "내가 틀렸던 것" 1건. 파일 모드나 JSON에서 만난 에러 화면을 그대로 붙이고 원인을 한 문장으로 적어라. ③ 타 과목 연결 1건. 화공양론 시험범위 노트에 복습 간격을 실제로 적용한 기록을 남겨라. 주당 의미 커밋 1건 이상이 채점 지표이며, 다음 주에 위키 마일스톤①(2점)의 첫 집계가 돌아간다.
5장 생성 코드 읽기 I: 코드를 말로 설명하기
이번 주의 도구: ④ 단위환산 + 물성 조회기
다음 상황을 생각해 보자. 유체역학 과제에 물의 밀도가 필요한 한 학생이 AI에게 물어 즉시 답을 받았고, 압력 단위를 psi에서 kPa로 바꾸는 코드도 AI가 10초 만에 만들어 주었다. 실행하니 숫자가 나왔고, 과제는 제출되었다. 그런데 검토 시간에 조교가 화면의 세 번째 줄을 가리키며 물었다. "이 줄은 무엇을 하는 겁니까?"
학생은 대답하지 못했다. 코드는 돌아갔지만, 그 코드가 압력을 곱하는지 나누는지, 어느 줄에서 단위가 바뀌는지 설명할 수 없었다. 돌아가는 코드와 이해한 코드는 다르다. 이 장은 그 간극을 메우는 기술, 곧 코드를 말로 설명하는 훈련을 다룬다.
이 장에서 다루는 것
- 코드를 자연어로 설명하는 EiPE 훈련법을 도입한다
- 변수·조건문·반복문·함수를 '읽기의 눈'으로 해부한다
- 리스트와 딕셔너리를 물성 데이터를 담는 최소 수준으로 다룬다
- CoolProp으로 물성을 조회하고 NIST 값과 대조하는 첫 화공 도구를 만든다
- 첫 30분 타이머와 공개 구술을 시작한다(마음의 준비를 포함해)
학습목표: 이 장을 마치면 다음을 할 수 있다
- AI가 생성한 코드의 목적·입력·처리·출력·한계를 한국어 2~3문장으로 설명할 수 있다.
- 변수 상태표를 그려 코드 실행을 한 줄씩 추적할 수 있다.
- 조건문과 반복문이 만드는 실행 경로를 읽고 출력을 예측할 수 있다.
- 함수의 첫 줄(시그니처)에서 입력과 출력의 계약을 읽어낼 수 있다.
- CoolProp 조회값과 NIST 대조값으로 AI가 제시한 물성·상수의 날조 여부를 판정할 수 있다.
- 30분 제한시간 안에서 커밋 규정을 지키며 작은 도구 하나를 완성할 수 있다.
이번 주 실습에서는 5.2~5.4절의 읽기 기술로 도구④ 단위환산 + 물성 조회기를 만든다. 이번 주부터 타이머(30분)와 공개 지목 구술이 시작된다. 매주 실습은 최종 시험의 축소판이며, 이번 주가 그 첫 리허설이다.
5.1 코드를 말로 설명한다는 것: EiPE
1주차의 역할 분담을 다시 꺼내자. 코드는 AI가 쓰고, 학생은 문제 분해·생성 코드 읽기·화학공학적 검증을 책임진다. 이 분담에서 "읽기"는 채점 대상이다. 최종 시험 루브릭의 코드 이해 10점은 무작위 지목 설명 4점, "이 줄을 바꾸면?" 예측 3점, 한계·개선점 3점으로 구성되고, 지목 질문 3개 중 2개 이상 설명에 실패하면 이 차원 전체가 0점이다.
코드 한 조각의 목적과 동작을 자연어로 설명하는 훈련을 EiPE(Explain in Plain English)라 한다. 컴퓨팅 교육 연구에서 코드 이해도를 측정하는 표준 문항 유형으로 쓰여 왔고, 이 수업에서는 시험1의 서술 문항과 매주 구술의 공통 형식이다. 프로그래밍 초보자가 LLM이 생성한 코드를 올바르게 이해하는 비율이 32.5%에 그쳤다는 보고(arXiv 2504.19037)가 있다. 셋 중 둘은 돌아가는 코드를 설명하지 못한 채 제출한다는 뜻이다.
- 문제 정의
- 분해
- 프롬프트
- 생성 코드 읽기
- 테스트
- 화공 검증
5.1.1 라인 낭독과 설명은 다르다
다음 함수를 보자. mmHg 단위의 압력을 bar로 바꾸는, AI가 생성했을 법한 네 줄짜리 코드다.
def mmhg_to_bar(p_mmhg):
"""mmHg 단위 압력을 bar로 환산한다."""
p_pa = p_mmhg * 133.322 # 압력 [Pa], 1 mmHg = 133.322 Pa
return p_pa / 1.0e5 # 압력 [bar], 1 bar = 1e5 Pa
이 코드에 대한 두 가지 설명을 비교해 보자.
나쁜 설명(라인 낭독): "p_mmhg에 133.322를 곱해서 p_pa에 넣고, p_pa를 1.0e5로 나눠서 반환한다."
좋은 설명(EiPE): "mmHg 단위의 압력을 받아 bar 단위로 바꿔 돌려주는 함수다. 중간에 Pa를 한 번 거쳐 두 단계로 환산한다. 음수 압력 같은 비물리적 입력은 걸러내지 않는다."
나쁜 설명은 코드를 소리 내어 읽었을 뿐이어서, 코드를 보지 않은 사람에게는 정보를 거의 주지 못한다. 좋은 설명은 코드 없이도 이 함수를 다시 만들 수 있을 만큼의 정보를 담는다. 무엇을 받아 무엇을 돌려주는지, 어떤 경로로 바꾸는지, 어떤 한계를 안고 있는지가 여기에 들어간다. 시험1의 EiPE 문항과 구술 평가의 지목 질문이 요구하는 것은 후자다.
좋은 설명에는 뼈대가 있다. 목적("무엇을 하는 함수인가"), 입력("무엇을, 어떤 단위로 받는가"), 처리("어떤 경로로 바꾸는가"), 출력("무엇을, 어떤 단위로 돌려주는가"), 한계("어떤 입력에서 무너지는가")의 다섯 요소다. 다섯 요소를 전부 갖추면 두세 문장이 된다. 이 공식은 장 끝의 요약에 S 5-1로 다시 나온다.
읽기가 쓰기보다 먼저인 이유는 이 수업의 구조 자체에 있다. 쓰기는 AI가 대신하지만, AI가 쓴 것이 맞는지 판정하는 읽기는 사람의 몫으로 남는다. 프로그래밍 문법을 다 배운 뒤에 읽기를 시작하지 않고, 읽기에 필요한 최소 문법(변수, 제어문, 함수)만을 이번 장에서 골라 배운다.
스스로 점검
- "p_pa를 1.0e5로 나눈다"는 문장은 EiPE 다섯 요소 중 무엇을 빠뜨리고 있는가?
- 최종 시험에서 지목 질문 3개 중 2개를 설명하지 못하면 코드 이해 차원의 점수는 몇 점인가?
5.2 변수 읽기: 이름, 값, 그리고 단위
값을 가리키는 이름을 변수(variable)라 한다. 코드를 읽는 일은 결국 각 변수가 어느 시점에 어떤 값을 가리키는지 따라가는 일이다. 아래 네 줄을 보자.
p_mmhg = 800.0 # 측정 압력 [mmHg]
PA_PER_MMHG = 133.322 # 환산계수 [Pa/mmHg]
p_pa = p_mmhg * PA_PER_MMHG # 압력 [Pa]
p_bar = p_pa / 1.0e5 # 압력 [bar], 1 bar = 1e5 Pa
print(f"{p_bar:.4f} bar") # 출력: 1.0666 bar
첫 줄은 p_mmhg라는 이름에 800.0이라는 값을 묶는다. 이런 문장을 할당(assignment)이라 한다. 둘째 줄의 PA_PER_MMHG는 환산계수를 담은 변수인데, 대문자 이름은 "이 값은 바꾸지 않겠다"는 관례적 신호다. 셋째 줄에서 곱셈이 일어나 새 변수 p_pa가 생기고, 넷째 줄에서 다시 p_bar가 생긴다.
실행 순서를 따라 변수의 값을 표로 적는 일을 상태 추적(tracing)이라 한다. 위 코드의 상태표는 다음과 같다.
| 실행 줄 | p_mmhg | p_pa | p_bar |
|---|---|---|---|
| 1행 후 | 800.0 | — | — |
| 3행 후 | 800.0 | 106657.6 | — |
| 4행 후 | 800.0 | 106657.6 | 1.0666 |
화공 코드의 변수에는 프로그래밍 교재에 없는 규율이 하나 더 붙는다. 물리량 변수에는 반드시 단위 주석을 단다는 규율이다. p = 800.0이라고만 적힌 코드에서 800이 mmHg인지 kPa인지 psi인지는 코드 어디에도 없다. 단위가 주석에 있어야 검증 루틴의 첫 항목인 단위 체크가 가능해진다.
단위를 추적하면 코드가 틀린 것도 잡아낸다. 단위환산 교재의 고전적 실수 사례를 코드로 옮겨 보자.
p_mmhg = 800.0 # 압력 [mmHg]
p_wrong = p_mmhg * 760.0 / 1.013 # 잘못된 환산: 단위가 [mmHg²/bar]가 된다
print(p_wrong) # 600197.4... — 비물리적 크기
환산계수 "1 atm = 760 mmHg = 1.013 bar"를 거꾸로 곱하면 값이 6.0×10⁵라는 터무니없는 크기가 되고, 단위는 mmHg²/bar라는 존재하지 않는 조합이 된다. 값을 보고 이상함을 느끼는 것이 극한값 감각이고, 단위를 적어 보고 오류를 확정하는 것이 단위 체크다. 3장에서 도입한 검증 루틴 3종이 코드 읽기에서 그대로 작동한다.
예제 5.2-1 생성 코드의 상태 추적과 EiPE 설명
문제 AI가 생성한 아래 코드 5-4를 실행하지 않고 읽어라. ① 변수 상태표를 만들고 ② 최종 출력을 예측한 뒤 ③ 코드 전체를 EiPE 다섯 요소로 2~3문장 설명하라.
풀이 전략 코드 생성은 이미 AI가 했으므로, 여기서 사람의 일은 읽기다. 위에서 아래로 한 줄씩 상태표를 채우고, 각 변수의 단위 주석을 따라가며 최종 단위를 확인한다.
t_f = 212.0 # 온도 [°F]
t_c = (t_f - 32.0) * 5 / 9 # 온도 [°C]
t_k = t_c + 273.15 # 온도 [K]
print(f"{t_k} K")
상태표를 채우면 t_f = 212.0 → t_c = 100.0 → t_k = 373.15가 되고, 출력은 373.15 K다. EiPE 설명은 이렇게 된다. "화씨 온도를 받아 섭씨를 거쳐 켈빈으로 바꿔 출력하는 스크립트다. 입력 212 °F는 코드에 박혀 있어 다른 온도를 쓰려면 첫 줄을 고쳐야 한다. 절대영도 아래의 비물리적 입력도 그대로 계산한다."
검증 루틴 3종
- 단위 체크: °F → °C → K로 각 줄의 주석과 수식이 일치한다.
- 대조값: 212 °F는 1 atm에서 물이 끓는 온도다. NIST WebBook의 물 끓는점 373.17 ± 0.04 K와 계산값 373.15 K가 부합한다.
- 극한값: t_f = 32.0을 넣으면 273.15 K(물의 어는점)가 나와야 한다. 손계산으로 확인된다.
분석 세 변수가 하나의 물리량(온도)을 단위만 바꿔 차례로 넘기는 구조다. 상태표 한 장이면 실행 없이도 출력을 확정할 수 있고, 이것이 시험1의 출력 예측 문항이 요구하는 능력이다. 코드 5-4의 첫 줄을 t_f = -500.0으로 바꿔 다시 추적해 보라. 코드는 절대영도 아래 온도도 그대로 출력하며, 이 한계가 5.3절의 조건문이 필요한 이유다.
스스로 점검
- 코드 5-2에서
p_pa의 단위는 코드의 어느 부분에서 결정되는가? - 코드 5-4에서
t_f = 32.0이면 출력은 무엇인가?
5.3 제어문 읽기: 갈림길과 반복
지금까지의 코드는 위에서 아래로 한 번씩만 실행됐다. 그러나 실제 코드는 "이 조건이 참이면 이것을 하고, 아니면 저것을 하라", "이 목록의 항목마다 같은 일을 반복하라" 같은 문장으로 흐름을 바꾼다. 조건에 따라 실행 경로를 가르는 문장을 조건문(conditional statement)이라 하고, 같은 블록을 여러 번 실행시키는 문장을 반복문(loop)이라 한다.
화공 도구에서 조건문의 첫 임무는 유효범위 검사다. 물성 상관식의 상수에는 반드시 유효 온도범위가 붙어 있고, 범위 밖의 온도로 값을 구하는 일을 외삽(extrapolation)이라 한다. 외삽을 조용히 허용하는 코드는 위험하므로, 좋은 도구는 계산 전에 범위를 검사한다.
t = 298.15 # 조회 온도 [K]
t_min, t_max = 379.0, 573.0 # 상수 유효범위 [K], NIST(Liu and Lindsay)
if t < t_min or t > t_max:
print("경고: 유효범위 밖 — 외삽 결과는 신뢰할 수 없다")
else:
print("유효범위 안")
if 다음의 t < t_min or t > t_max는 참 또는 거짓으로 계산되는 식이다. 참이면 바로 아래 들여쓰인 블록이 실행되고, 거짓이면 else 블록이 실행된다. 여기서는 298.15가 379보다 작으므로 조건이 참이 되어 경고가 출력된다. 들여쓰기가 곧 소속을 나타낸다. 어떤 줄이 어느 블록에 속하는지는 들여쓰기 깊이가 결정한다.
반복문은 같은 계산을 목록 전체에 적용한다. 여러 값을 순서대로 담는 자료형을 리스트(list)라 하며, 대괄호로 만든다.
pressures_psi = [10.0, 14.7, 30.0, 100.0] # 압력 목록 [psi]
KPA_PER_PSI = 6.895 # 환산계수 [kPa/psi]
for p in pressures_psi:
print(f"{p:6.1f} psi = {p * KPA_PER_PSI:8.1f} kPa")
for p in pressures_psi:는 리스트의 항목을 하나씩 꺼내 p에 담고, 들여쓰인 블록을 항목 수만큼(여기서는 네 번) 실행한다. 둘째 항목 14.7 psi는 101.4 kPa로 환산되는데, 이는 1 atm(101.325 kPa)과 거의 같다. 표 하나를 출력했을 뿐인데 대조값 검증이 공짜로 따라온 셈이다.
실행 흐름을 읽는 요령은 "다음에 실행될 줄이 몇 번째 줄인가"를 계속 자문하는 것이다. 조건문에서는 어느 가지로 가는지, 반복문에서는 몇 바퀴째인지를 상태표 옆에 함께 적으면 된다. 흐름 추적은 유용하지만 코드가 길어지면 금세 벅차진다. 그때 필요한 것이 다음 절의 함수 단위 읽기다.
예제 5.3-1 온도 구간별 상수 선택 코드의 출력 예측
문제 아래 코드 5-7은 조회 온도에 따라 물의 Antoine 상수 묶음을 골라 증기압을 계산한다. T = 320 K일 때 ① 어느 가지가 실행되는지 ② 출력이 대략 얼마인지 예측하고 ③ 이 코드의 한계를 한 문장으로 말하라. 상수는 표 5-2에서 왔다.
풀이 전략 조건문 읽기의 정석대로 위에서부터 조건식을 하나씩 평가한다. 320이 각 구간에 속하는지만 판정하면 가지가 정해지고, 나머지는 변수 추적이다.
t = 320.0 # 조회 온도 [K]
# 물의 Antoine 상수 (log10, p*[bar], T[K]) — 표 5-2, NIST WebBook
if 273.0 <= t <= 303.0:
a, b, c = 5.40221, 1838.675, -31.737
elif 304.0 <= t <= 333.0:
a, b, c = 5.20389, 1733.926, -39.485
elif 334.0 <= t <= 363.0:
a, b, c = 5.07680, 1659.793, -45.854
else:
raise ValueError("유효범위 밖 온도")
p_bar = 10 ** (a - b / (t + c)) # 증기압 [bar]
print(f"{p_bar:.3f} bar")
320은 첫 구간(273~303)에 속하지 않고 둘째 구간(304~333)에 속하므로 둘째 가지가 실행된다. 손계산으로 10^(5.20389 − 1733.926/280.515) ≈ 0.105 bar를 얻는다.
검증 루틴 3종
- 단위 체크: 상수 묶음이 bar·K 기준(log₁₀)임이 주석에 명시되어 있고, t의 단위 K와 일치한다.
- 대조값: 320 K(약 47 °C)의 물은 끓지 않으므로 증기압은 1 atm(1.013 bar)보다 훨씬 작아야 한다. 0.105 bar는 이 상식과 부합한다.
- 극한값: t = 380.0을 넣으면 세 구간 어디에도 속하지 않아 ValueError가 발생한다. 외삽을 조용히 허용하지 않는 설계다.
분석 조건문 읽기의 핵심 질문은 "이 입력이면 어느 가지인가"와 "어느 가지에도 안 걸리면 무슨 일이 나는가"의 두 개다. 이 코드는 범위 밖에서 에러를 일으키도록 마지막 else를 두었는데, AI가 생성한 코드에는 이 안전망이 빠져 있는 경우가 잦다. t = 340.0과 t = 365.0으로 바꿔 각각 어느 가지로 가는지 예측한 뒤 실행으로 확인해 보라.
| 유효범위 [K] | A | B | C | 원자료 |
|---|---|---|---|---|
| 273~303 | 5.40221 | 1838.675 | −31.737 | Bridgeman and Aldrich (1964) |
| 304~333 | 5.20389 | 1733.926 | −39.485 | Bridgeman and Aldrich (1964) |
| 334~363 | 5.07680 | 1659.793 | −45.854 | Bridgeman and Aldrich (1964) |
| 379~573 | 3.55959 | 643.748 | −198.043 | Liu and Lindsay (1970) |
표 5-2에는 이 교재의 상수 표기 의무 네 가지(로그 밑, 압력 단위, 온도 단위, 유효범위)가 전부 들어 있다. 넷 중 하나라도 빠진 상수 표는 쓰지 않는다. 마지막 행의 상수를 유효범위 아래인 373 K에 적용하면 0.76 bar가 나오는데, 물이 1 atm(1.013 bar)에서 끓는다는 상식과 25% 어긋난다. 외삽의 대가는 8장에서 정량적으로 다룬다.
스스로 점검
- 코드 5-7에서 t = 273.0이면 어느 가지가 실행되는가? t = 272.9라면?
- 코드 5-6의 반복 횟수를 결정하는 것은 코드의 어느 부분인가?
5.4 함수 읽기: 입력·변환·출력의 계약
이름을 붙여 두고 필요할 때마다 불러 쓰는 코드 묶음을 함수(function)라 한다. 함수에 넘겨주는 값을 인자(argument), 함수가 돌려주는 값을 반환값(return value)이라 한다. 함수의 첫 줄(이름과 인자 목록)은 "무엇을 주면 무엇을 돌려주겠다"는 계약서이고, 함수 읽기는 이 계약서 읽기에서 시작한다.
def mmhg_to_bar(p_mmhg):이라는 첫 줄만 보고도 세 가지를 읽어낼 수 있다. mmHg를 bar로 바꾸는 일을 하고, 압력 하나를 받으며, 이름의 방향(mmhg→bar)이 환산 방향을 약속한다. 본문을 읽기 전에 계약부터 읽는 습관은 코드가 길어질수록 도움이 된다. 흐름 추적이 벅찬 큰 코드도 함수 단위로 끊으면 계약들의 연결로 읽힌다.
실제로 AI에게 함수를 시키고 그 결과를 읽어 보자. 프롬프트-응답 리스팅의 3단 형식은 2장에서 선언한 그대로다.
압력을 psi에서 kPa로 바꾸는 파이썬 함수를 만들어 줘. 함수 이름은 psi_to_kpa, 주석은 한국어로, 환산계수에 단위 주석을 달아 줘.
def psi_to_kpa(p_psi):
"""psi 단위 압력을 kPa로 환산한다."""
KPA_PER_PSI = 6.895 # 환산계수 [kPa/psi]
return p_psi * KPA_PER_PSI # 압력 [kPa]
✔ 환산계수 6.895 kPa/psi가 화공양론 교재의 값과 일치. psi_to_kpa(14.7) → 101.36 kPa로 1 atm(101.325 kPa)과 부합한다(대조값 통과). 다만 음수 압력을 거르지 않으므로 유효성 검사를 다음 프롬프트에서 추가 요구했다.
검증 코멘트가 짚은 대로, 생성된 함수는 계약을 지키지만 비물리적 입력에 침묵한다. "절대압이 음수면 ValueError를 일으켜 줘"라고 한 줄 더 요구하면 5.3절의 조건문이 함수 안으로 들어온다. 프롬프트 → 읽기 → 결함 발견 → 프롬프트 개선의 사이클이 한 바퀴 돈 것이다.
5.4.1 딕셔너리: 이름으로 값을 꺼내는 상자
물성 조회기는 밀도·끓는점처럼 여러 값을 한꺼번에 다룬다. 이름표(키)로 값을 꺼내는 자료형을 딕셔너리(dictionary)라 하며, 중괄호로 만든다.
props = {"T_boil_K": 373.12, "rho_kg_m3": 997.0} # 물성 이름 → 값
print(props["T_boil_K"]) # 키로 조회: 373.12
리스트가 순서(0번째, 1번째)로 값을 꺼낸다면 딕셔너리는 이름으로 꺼낸다. props["rho_kg_m3"]처럼 키에 단위를 새겨 두는 것도 단위 주석 규율의 연장이다. 이 장에서 리스트와 딕셔너리는 여기까지만 필요하다. 항목을 담고, 꺼내고, 반복문으로 순회하는 수준이면 도구④를 읽고 만들 수 있다.
5.4.2 CoolProp: 물성을 조회하는 함수 라이브러리
물성값 자체를 AI에게 묻는 것은 위험하다는 사실을 3장에서 보았다. 대안은 검증된 물성 라이브러리를 코드로 조회하는 것이다. CoolProp은 순수 유체의 열역학 물성을 계산해 주는 오픈소스 라이브러리로, PropsSI 함수 하나로 대부분의 조회가 끝난다.
예제 5.4-1 CoolProp 물성 조회 함수 만들기와 검증
문제 압력을 받아 물의 끓는점을 돌려주는 함수를 AI로 생성하고, 1 atm에서의 반환값을 NIST 대조값으로 검증하라.
풀이 전략 함수 생성은 AI에게 맡기고("CoolProp의 PropsSI로 압력[Pa]을 받아 물의 포화온도[K]를 반환하는 함수를 만들어 줘"), 사람은 반환값의 계약(단위)과 대조값 검증을 맡는다.
from CoolProp.CoolProp import PropsSI
def water_boiling_t(p_pa):
"""압력 p_pa[Pa]에서 물의 끓는점[K]을 반환한다."""
return PropsSI("T", "P", p_pa, "Q", 0, "Water") # 포화온도 [K]
t_b = water_boiling_t(101325.0) # 1 atm = 101325 Pa
print(f"{t_b:.2f} K") # 출력: 373.12 K
PropsSI의 인자를 계약으로 읽으면 이렇다. 첫 인자 "T"는 구하려는 물성(온도), 이어지는 "P"와 값은 주어진 조건(압력), "Q", 0은 포화액 상태라는 지정, 마지막이 물질명이다. 모든 입출력은 SI 단위다.
검증 루틴 3종
- 단위 체크: 입력 Pa, 출력 K다. PropsSI는 SI 단위만 쓰므로 101325를 kPa로 착각해 넘기면 즉시 어긋난다.
- 대조값: NIST WebBook의 물 끓는점 373.17 ± 0.04 K. 반환값 373.12 K와의 차이는 0.05 K(0.013%) 수준이다.
- 극한값: 압력을 낮춰 water_boiling_t(50000.0)을 부르면 끓는점이 373 K보다 내려가야 한다. 감압하면 낮은 온도에서 끓는다는 상식과 방향이 맞는지 확인하라.
분석 이 예제의 교훈은 분업이다. 함수 골격은 AI가, 물성값은 CoolProp이, 판정은 사람이 맡는다. 세 주체 중 어느 하나가 나머지를 대신하기는 어렵다. 물질명을 "Ethanol"로 바꿔 재실행하고, 반환값이 물보다 낮은지 확인해 보라. 에탄올이 물보다 먼저 끓는다는 일반화학 상식과 맞는지 보면 된다.
생각해보기
AI가 준 물성값의 진위를 판단할 방법을 두 가지 제안해 보라. 인터넷이 되지 않는 시험장이라면 두 방법 중 무엇이 살아남는가?
스스로 점검
- psi_to_kpa(-10.0)은 무엇을 반환하는가? 그 값은 물리적으로 말이 되는가?
- 코드 5-9에서 373.12를 돌려주는 조회식은 무엇인가?
5.5 첫 타이머, 첫 공개 구술: 마음의 준비
이번 주 실습부터 두 가지가 바뀐다. 첫째, 과제에 30분의 제한시간이 걸린다. 둘째, 실습 마무리에 무작위로 지목된 학생이 자기 코드 한 부분을 모두 앞에서 1분간 설명한다. 1~4주의 실습이 시간 무제한에 페어 안에서만 설명하는 저부담 구간이었다면, 5주차는 시험 형식으로 넘어가는 첫 계단이다.
제한시간은 1장에서 안내한 대로 최종 시험의 120분까지 매주 조금씩 늘어난다. 처음부터 2시간짜리 압박을 주는 대신 시간 감각을 단계적으로 키우는 설계다. 30분 안에 도구④를 완성하지 못해도 성적에는 아무 일도 일어나지 않는다. 이번 주 결과는 등수 없이 통과/재도전의 2단계로만 처리되고, 재도전은 다음 실습 전까지 열려 있다.
공개 구술이 두려운 것은 정상이다. 다만 이 장치의 정체를 알면 부담이 줄어든다. 구술은 4장에서 배운 인출연습의 사회적 버전이다. 머릿속에 있다고 믿는 지식을 꺼내 보는 행위 자체가 기억을 강화하며, 설명하다 막히는 지점이 곧 공부할 지점이다. 지금 사람들 앞에서 막히는 데에는 아무 대가가 없지만, 최종 시험의 구술 방어에서 막히면 점수로 이어진다. 매주 반복되는 리허설이 그 부담을 미리 덜어 준다.
준비 방법은 단순하다. 실습 중 함수 하나를 완성할 때마다 소리 내지 않아도 좋으니 S 5-1의 순서(목적→입력→처리→출력→한계)로 속으로 한 번 설명해 본다. 퇴실 전 페어에게 1분 설명을 한 번 하고 나가면 그 주의 구술 리허설은 끝난 셈이다. 막히면 침묵하지 말고 "목적부터 다시 말하겠습니다"라고 처음으로 돌아가면 된다. 구조를 따라 말하는 훈련이다.
생각해보기
타이머가 5분 남았는데 도구가 미완성이다. 남은 5분을 기능 추가에 쓰는 것과 README 검증 기록·커밋에 쓰는 것 중 어느 쪽이 유리한가? 최종 시험 루브릭(작동성 14점에 검증 증빙 필수, 30분 스냅샷 커밋이 부분 채점 근거)을 근거로 판단해 보라.
실습 5: 도구④ 단위환산 + 물성 조회기 (30분)
준비물
- 스타터 repo(조교 배포)를 클론한 상태. (성공 시
tool04/폴더와test_tool04.py가 보인다) - CoolProp 설치: 터미널에서
pip install CoolProp. (확인:python -c "import CoolProp"이 아무 오류 없이 끝난다) - 3주차에 만든 검증 README 양식. 이번 주 검증 기록도 같은 3종 형식으로 쓴다.
- 타이머는 강의실 공용으로 30분. 시작과 동시에 출발한다.
과제
- 실습 5.1. repo에 빈 상태로 첫 커밋을 남겨라. (성공 시 커밋 로그에 시작 시각이 찍힌다. 최종 시험의 "빈 repo 첫 커밋" 규정 리허설이다)
- 실습 5.2. 단위환산 함수 3종(psi→kPa, mmHg→bar, °C→K)을 AI로 생성하고, 각 함수 위에 EiPE 설명을 한국어 주석 2~3문장으로 직접 써넣어라. (완료 기준: 세 함수 모두 단위 주석과 EiPE 주석이 있다) 커밋하라.
- 실습 5.3. CoolProp으로 물질명·온도·압력을 받아 밀도와 끓는점을 딕셔너리로 반환하는 조회 함수를 생성하고 물에 대해 실행하라. (성공 시 끓는점이 373 K 부근으로 나온다)
- 실습 5.4. AI에게 물의 Antoine 상수를 물어라. 받은 상수로 373.15 K의 증기압을 계산해 1.013 bar와 비교하고, NIST WebBook의 표 5-2와 대조해 날조·불일치 여부를 README에 기록하라. (완료 기준: 판정과 근거 수치가 README에 있다)
- 실습 5.5. 검증 루틴 3종(단위·대조값·극한값)을 README에 채우고 최종 커밋을 남겨라. (성공 시
pytest test_tool04.py의 스모크 테스트 3개가 통과한다)
완성 기준
- 스모크 테스트 3개 통과 (
pytest test_tool04.py) - README에 검증 루틴 3종 기록 + 실습 5.4의 날조 판정 기록
- 30분 내 커밋 2회 이상 (시작 커밋 포함)
- 미완성이면 재도전한다. 다음 실습 전까지 같은 기준으로 다시 제출한다
막혔는가? CoolProp이 설치되지 않을 때
에러 메시지를 요약하지 말고 전문 그대로 AI에게 붙여넣고 이렇게 물어라. "이 에러의 원인 후보 3개와 각각의 확인 방법을 알려 줘. 내 환경은 Windows, pip 사용."
막혔는가? PropsSI 인자 순서가 헷갈릴 때
"CoolProp PropsSI로 물의 1 atm 끓는점을 구하는 최소 예제를 보여 주고, 인자 각각이 무엇을 뜻하는지 한 줄씩 설명해 줘"라고 요구하라. 설명 없는 코드만 주면 설명을 다시 요구하라.
막혔는가? 내 코드를 설명할 수 없을 때
"이 함수를 공학도 저학년에게 설명하듯 세 문장으로 설명해 줘. 그다음 내가 내 말로 다시 설명할 테니 틀린 부분을 지적해 줘"라고 하라. AI의 설명을 그대로 외우는 것이 아니라, 내 설명을 AI로 검증하는 방향이다.
퇴실 전 위키 기록 (15분)
오늘 만든 것 1건, 막힌 것 1건, 틀린 것 1건을 위키에 커밋해야 퇴실한다. 이번 주는 위키 마일스톤①(2점) 채점 주간이다. 1~5주의 커밋 리듬과 "내가 틀렸던 것" 기록이 자동 집계된다.
요약
- S 5-1 EiPE 설명 공식: 목적 → 입력 → 처리 → 출력 → 한계. 다섯 요소를 채우면 두세 문장의 설명이 된다.
- S 5-2 Antoine 식 (5.1): log₁₀ p* = A − B/(T + C). 상수를 받으면 로그 밑·압력 단위·온도 단위·유효범위 4종을 먼저 확인한다.
- S 5-3 물성 프롬프트 패턴: 값을 묻지 말고 "NIST/CoolProp에서 조회·검증하는 코드"를 요구한다.
| 요소 | 읽을 때 던지는 질문 | 시험1 출제 형태 |
|---|---|---|
| 변수 | 이 이름은 지금 어떤 값·단위를 가리키는가 | 상태표 채우기, 출력 예측 |
| 조건문 | 이 입력이면 어느 가지인가, 안 걸리면 무슨 일이 나는가 | 가지 판정, 경계값 추적 |
| 반복문 | 몇 번 도는가, 매 바퀴 무엇이 변하는가 | 반복 횟수·누적값 예측 |
| 함수 | 무엇을 주면 무엇을 어떤 단위로 돌려주는가 | EiPE 설명, "이 줄을 바꾸면?" |
AI 협업 사이클의 ④ 생성 코드 읽기 블록이 이번 장에서 처음으로 본격 훈련되었고, 도구 사다리는 학습 OS 3종에 이어 첫 화공 도구인 도구④까지 왔다. 읽기는 이제 시작이다. 읽을 수 있어야 다음 장에서 AI의 틀린 답을 채점하고 고칠 수 있다.
용어 정리
- EiPE (Explain in Plain English)
- 코드의 목적과 동작을 자연어로 설명하는 훈련·문항 유형.
- 변수 (variable)
- 값을 가리키는 이름.
- 할당 (assignment)
- 이름에 값을 묶는 문장.
- 상태 추적 (tracing)
- 실행 순서를 따라 각 변수의 값을 표로 적으며 코드를 읽는 방법.
- 조건문 (conditional statement)
- 조건의 참·거짓에 따라 실행 경로를 가르는 문장.
- 반복문 (loop)
- 같은 블록을 여러 번 실행시키는 문장.
- 리스트 (list)
- 여러 값을 순서대로 담는 자료형.
- 딕셔너리 (dictionary)
- 키(이름표)로 값을 꺼내는 자료형.
- 함수 (function)
- 이름을 붙여 두고 필요할 때 불러 쓰는 코드 묶음.
- 인자 (argument)
- 함수에 넘겨주는 값.
- 반환값 (return value)
- 함수가 돌려주는 값.
- 외삽 (extrapolation)
- 상관식을 유효범위 밖에 적용해 값을 구하는 일.
- CoolProp
- 순수 유체의 열역학 물성을 계산하는 오픈소스 라이브러리.
연습문제
Q군: 개념·읽기 (AI 없이 푼다)
- Q5.1 코드 5-2에서
p_mmhg = 760.0으로 바꾸었을 때의 출력을 실행 없이 예측하고, 그 값이 어떤 대조값과 일치해야 하는지 밝혀라. - Q5.2 켈빈 온도를 받아 섭씨로 바꿔 반환하는 함수
k_to_c(t_k)가 주어졌다고 하자. 이 함수를 EiPE 다섯 요소로 2~3문장 설명하라. - Q5.3 코드 5-7에서 t = 273.0일 때와 t = 272.9일 때 각각 무슨 일이 일어나는지 실행 경로를 따라 설명하라.
- Q5.4 단위환산 함수를 일부러 틀리게 만드는 방법을 두 가지 제시하고, 각각이 검증 루틴 3종 중 무엇에 걸리는지 밝혀라.
P군: 제작·계산 (AI 사용 전제)
- P5.1 도구④에 길이 환산(ft→m)을 추가하라. 1 ft = 0.3048 m를 대조값으로 쓰고, 검증 README를 갱신하라.
- P5.2 Antoine 상수 자동 검증기를 만들어라. 상수 A, B, C와 유효범위를 받아 373.15 K의 계산 증기압이 1.013 bar의 ±5% 안이면 PASS, 아니면 FAIL을 출력한다(유효범위가 373 K를 포함하지 않으면 SKIP). 표 5-2의 진짜 상수와 본문 경고 박스의 날조 상수로 시험하고, 결과를 EiPE 설명과 함께 위키에 기록하라.
위키 기록 과제 (이번 주 의미 커밋 ≥1이 채점 지표다)
① 이 장의 핵심(EiPE 다섯 요소와 문법 요소별 읽기 질문)을 1페이지로 정리하라(#seed). ② 이번 주 "내가 틀렸던 것" 1건을 에러 화면과 함께 기록하라. ③ 타 과목 연결 1건. 화공양론이나 유체역학 과제에서 단위환산이 필요했던 장면을 찾아 도구④로 다시 풀어 보고 결과를 남겨라. 이번 주는 위키 마일스톤①(2점) 채점 주간이다.
6장 생성 코드 읽기 II: AI 오답 채점과 교정
이번 주의 실습: 모의 시험1. 시험1(7주차)의 지필 리허설이다
다음 상황을 생각해 보자. 화공양론 과제를 검산하려고 AI에게 "25 °C, 1 atm에서 이상기체 1 mol의 부피를 리터로 구해 줘"라고 요청했다. 몇 초 만에 깔끔한 함수가 도착했고, 실행하니 "부피: 2.05 L"가 출력됐다. 에러는 한 줄도 없었다.
그런데 일반화학에서 외운 숫자가 마음에 걸린다. 0 °C, 1 atm에서 기체 1 mol은 약 22.4 L를 차지한다. 온도가 더 높아졌는데 부피가 10분의 1로 줄어들 수 있는가. 코드가 돌아갔다는 사실과 답이 맞다는 사실은 별개의 문제다. 이 장은 그 간극을 채점자의 눈으로 메우는 기술을 다룬다.
이 장에서 다루는 것
- 오류의 세 유형(문법·런타임·의미 오류)과 각각이 드러나는 방식을 구분한다
- 트레이스백을 아래에서 위로 읽어 오류의 병명과 위치를 찾는 절차를 익힌다
- AI가 낸 틀린 화공 계산을 검증 루틴 3종으로 채점·교정하는 프로토콜을 연습한다
- 가설 기반 디버깅 프롬프트 3패턴을 정리한다
- 모의 시험1을 치르고 자가 진단표를 만든다
학습목표 이 장을 마치면 다음을 할 수 있다.
- 주어진 오류 사례를 문법 오류·런타임 오류·의미 오류로 분류할 수 있다.
- 트레이스백에서 예외 이름·발생 위치·호출 경로를 지목할 수 있다.
- AI가 낸 화공 계산 풀이를 검증 루틴 3종으로 채점하고 판정 근거를 쓸 수 있다.
- 틀린 코드에 대해 진단 프롬프트와 교정 프롬프트를 구분해 작성할 수 있다.
- 모의 시험 결과를 자가 진단표로 분류하고 시험1 대비 보완 계획을 세울 수 있다.
이번 주 실습은 도구 제작이 아니라 모의 시험1이다. 7주차 시험1(20점, AI 전면 금지, 오프라인 지필)의 무채점 리허설이다. §6.1~6.4가 시험1 문항 유형의 본체를 훈련하고, §6.5가 시험 규정을 안내한다. 오답 리뷰에서 작성하는 자가 진단표는 7장의 복습 지도로 그대로 이어진다.
6.1 오류의 세 얼굴: 문법, 런타임, 의미
프로그램이 틀리는 방식은 크게 세 가지다. 셋은 드러나는 시점이 다르고, 파이썬이 도와주는 정도와 위험한 정도도 다르다. 채점의 첫 단계는 지금 눈앞의 오류가 셋 중 무엇인지 판별하는 일이다.
파이썬 문법 규칙을 어겨 실행이 아예 시작되지 못하는 오류를 문법 오류(syntax error)라 한다. 괄호 짝이 안 맞거나 if 뒤의 콜론을 빠뜨린 경우가 대표적이다. 파이썬은 프로그램을 시작하기 전에 멈춰 서서 위치를 알려 주므로, 셋 중 가장 값싸게 고칠 수 있다.
실행 도중 더 진행할 수 없는 상황을 만나 프로그램이 멈추는 오류를 런타임 오류(runtime error)라 한다. 0으로 나누거나, 정의한 적 없는 이름을 부르는 경우다. 이때 파이썬이 발생시키는 신호를 예외(exception)라 하며, 예외가 나면 파이썬은 무엇이 어디에서 잘못됐는지를 담은 보고서를 출력한다. 이 보고서를 읽는 법이 6.2절의 주제다.
프로그램이 끝까지 돌고 출력도 내놓지만, 의도한 것과 다른 일을 하는 오류를 의미 오류(semantic error)라 한다. 장 첫머리의 2.05 L가 정확히 이것이다. 파이썬은 오류를 알리지 않았다. 시킨 계산을 충실히 수행했을 뿐이고, 틀린 것은 계산의 설계였다.
| 유형 | 드러나는 시점 | 파이썬이 알려주는가 | 화공 계산에서의 예 |
|---|---|---|---|
| 문법 오류 | 실행 시작 전 | 알려준다 (위치 표시) | 괄호 누락으로 실행 불가 |
| 런타임 오류 | 실행 도중 | 알려준다 (트레이스백) | 몰분율 계산에서 총 몰수 0으로 나눔 |
| 의미 오류 | 드러나지 않을 수 있음 | 알려주지 않는다 | 섭씨 온도를 절대온도 자리에 대입 |
앞의 두 오류는 시끄럽다. 파이썬이 실행을 멈추고 오류를 알리므로 존재를 놓칠 수 없다. 의미 오류는 조용하다. 멀쩡해 보이는 숫자가 보고서와 설계 계산 속으로 흘러 들어간다. 셋 중 화학공학자에게 가장 위험한 것은 의미 오류다.
AI 생성 코드에서 이 균형은 의미 오류 쪽으로 더 쏠린다. AI는 방대한 코드로 훈련되어 문법적으로는 매끈한 코드를 내놓는다. 실행이 되니 맞아 보이고, 맞아 보이니 검증을 건너뛰게 된다. 프로그래밍 입문자가 AI 생성 코드를 정확히 이해한 비율이 32.5%에 그쳤다는 조사(arXiv 2504.19037)를 5장에서 보았다. 읽지 못한 코드 속의 의미 오류는 잡아내기 어렵다.
3장의 환각 문제와도 이어진다. AI가 날조한 물성값이나 존재하지 않는 함수가 코드에 들어오면, 어떤 것은 시끄러운 런타임 오류로, 어떤 것은 조용한 의미 오류로 나타난다. 다음 사례는 시끄러운 쪽이다. 그래서 오히려 다행인 경우다.
스스로 점검
- 코드가 끝까지 실행되고 그럴듯한 값을 출력했다. 이때 아직 배제되지 않은 오류 유형은 무엇인가?
- AI 생성 코드에서 문법 오류가 드문 이유를 한 문장으로 설명하라.
해답은 권말 부록에 있다.
6.2 트레이스백: 에러 메시지를 읽는 법
런타임 오류를 직접 만나 보자. 코드 6-1은 이상기체의 몰부피를 구하려는 함수인데, 안에 실수가 하나 심어져 있다.
R = 8.314 # J/(mol·K), 출처: NIST CODATA
def molar_volume(T, P):
"""이상기체 몰부피 [m^3/mol]. 입력: T[K], P[Pa]"""
return R * T_kelvin / P
v = molar_volume(298.15, 101325.0)
print(v)
실행하면 계산 결과 대신 다음 보고서가 출력된다. 요약하지 않고 전문을 싣는다.
Traceback (most recent call last):
File "molar_volume.py", line 7, in <module>
v = molar_volume(298.15, 101325.0)
File "molar_volume.py", line 5, in molar_volume
return R * T_kelvin / P
NameError: name 'T_kelvin' is not defined
런타임 오류가 나면 파이썬은 예외의 이름, 문제가 일어난 줄, 그리고 그 줄에 도달하기까지의 함수 호출 경로를 담은 보고서를 출력한다. 이 보고서를 트레이스백(traceback)이라 한다. 첫 줄의 "most recent call last"가 읽기 방향을 알려 준다. 사고 현장인 가장 최근의 호출이 맨 아래에 있다.
그래서 트레이스백은 아래에서 위로 읽는다. 첫째, 마지막 줄을 본다. NameError가 병명이고, name 'T_kelvin' is not defined가 증상 설명이다. 둘째, 바로 위 블록에서 발병 위치를 확인한다. molar_volume.py의 5행, molar_volume 함수 안이다. 셋째, 더 위 블록으로 올라가면 그 함수를 누가 불렀는지가 나온다. 7행의 최상위 호출이다.
병명과 위치가 나왔으니 원인 진단은 짧다. 함수의 매개변수 이름은 T인데 본문에서는 T_kelvin이라는, 어디에서도 정의된 적 없는 이름을 썼다. T_kelvin을 T로 고치거나 매개변수 이름을 T_kelvin으로 바꾸면 해결된다. 파이썬 버전에 따라 문제 지점 아래에 ^ 기호가 추가로 표시되기도 하는데, 읽는 순서는 같다.
예외의 이름은 원인의 범주를 바로 좁혀 준다. 자주 만나는 여섯 가지를 표 6-2에 모았다. 시험1에서도, 앞으로의 모든 실습에서도 이 여섯이 마주칠 예외의 대부분을 차지한다.
| 예외 이름 | 뜻 | 흔한 원인 |
|---|---|---|
NameError | 정의되지 않은 이름을 사용 | 변수명 오타, 정의 전에 사용 |
TypeError | 자료형이 연산과 맞지 않음 | 문자열과 수를 연산, 인자 개수 잘못 |
ValueError | 형은 맞으나 값이 부적절 | float()에 숫자가 아닌 문자열 전달 |
IndexError | 인덱스가 범위를 벗어남 | 리스트 길이 이상의 인덱스 접근 |
KeyError | 딕셔너리에 없는 키 조회 | 물질명 오타, 등록 안 된 단위 기호 |
ZeroDivisionError | 0으로 나눔 | 총량이 0인 입력으로 분율 계산 |
이 중 ValueError는 화공 데이터 처리에서 유난히 자주 만난다. 센서 로그나 CSV에서 읽은 값에는 단위 문자가 붙어 있는 경우가 많기 때문이다. 대화형 셸에서 직접 재현해 보자.
>>> float('25 C')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: could not convert string to float: '25 C'
'25 C'는 문자열로서는 멀쩡하지만 수로 변환될 수 없는 값이다. 형은 맞고 값이 문제이므로 TypeError가 아니라 ValueError다. 이 구분이 되면 교정 방향이 자동으로 나온다. 단위 문자를 떼어내는 전처리가 필요하다.
마지막 규칙 하나. 에러 메시지는 요약하거나 의역하지 말고 전문 그대로 다뤄라. 검색할 때도, AI에게 물을 때도, 위키에 기록할 때도 원문 문자열이 그대로 있어야 같은 오류를 겪은 다른 사람의 해법과 연결된다. 지면의 트레이스백을 전부 전문으로 싣는 이유가 이것이다.
스스로 점검
- 트레이스백에서 가장 먼저 읽어야 할 줄은 어디이고, 거기에서 무엇을 얻는가?
float('3.5e2')와float('350 K')중 예외를 일으키는 쪽은 어느 것이며, 예외 이름은 무엇인가?
6.3 오답의 채점: 검증 루틴을 채점 기준으로 바꾼다
- 문제 정의
- 분해
- 프롬프트
- 생성 코드 읽기
- 테스트
- 화공 검증
3주차에 도입한 검증 루틴 3종(단위 체크, 대조값, 극한값)은 지금까지 자신의 코드를 지키는 방패였다. 이번 절에서는 같은 루틴을 AI의 풀이를 채점하는 잣대로 돌려세운다. 방패가 채점 기준표가 되는 셈이다.
채점은 네 단계 절차로 고정한다. 첫째, 실행하기 전에 수식과 단위를 손으로 추적한다. 각 변수에 단위를 붙여 보고 등호 양변의 단위가 맞는지 본다. 둘째, 대조값 1점을 정한다. 교과서나 NIST에서 이미 아는 값 하나를 고른다. 셋째, 극한값을 머리로 대입해 본다. 0, 음수, 아주 큰 값에서 물리적으로 말이 되는 답이 나오는 구조인가. 넷째, 판정문을 쓴다. 틀린 줄 번호, 원인, 교정 방향을 완결 문장으로 적는다.
시험1의 문항 유형 ③ "AI가 낸 오답을 채점·교정하라"가 요구하는 것이 정확히 이 절차다. 지필 시험이므로 실행 단계가 없다. 컴퓨터 없이 ①~④를 해내는 훈련이 이 절의 목표다. 장 첫머리의 사건으로 돌아가 실제로 채점해 보자.
예제 6.3-1 AI가 낸 이상기체 부피 풀이를 채점하라
문제 아래 프롬프트에 대해 AI가 생성한 코드와 실행 결과가 주어져 있다. 이 풀이를 채점 4단 절차로 채점하고, 틀렸다면 원인과 교정 방향을 판정문으로 써라. 이상기체 상태방정식은 다음과 같다.
PV = nRT (6.1)
풀이 전략 시험1과 같은 조건(실행 없이 지필)으로 채점한다. 식 (6.1)에서 기체상수 R = 8.314 J/(mol·K)의 단위 분모에 K가 있으므로, 온도 자리에 들어가는 값의 단위부터 추적한다. 대조값으로는 일반화학의 상식을 쓴다. 0 °C, 1 atm(= 101.325 kPa)에서 이상기체 1 mol의 부피는 약 22.4 L다.
25 °C, 1 atm에서 이상기체 1 mol의 부피를 리터로 구하는 파이썬 코드를 짜 줘.
def ideal_gas_volume(n, T, P):
"""이상기체 부피를 계산한다."""
R = 8.314 # 기체상수
V = n * R * T / P
return V
# 25 °C, 1 atm에서 1 mol
volume = ideal_gas_volume(1.0, 25, 101325)
print(f"부피: {volume * 1000:.2f} L")
✔ 실행 결과: 부피: 2.05 L. 식의 형태는 (6.1)과 일치하고 압력은 Pa로 옳게 들어갔다. 그러나 8행에서 T 자리에 섭씨 값 25를 그대로 넘겼고, 함수 안 어디에도 절대온도 변환이 없다. 주석에 단위가 하나도 없다는 점이 이 실수를 숨겼다.
- 단위: R = 8.314 J/(mol·K)의 분모는 K인데 대입된 25는 °C다. 단위가 일치하지 않는다. 온도 변환식 T[K] = T[°C] + 273.15 (6.2) 가 코드에 없다.
- 대조값: 0 °C, 1 atm에서 약 22.4 L(일반화학 상식, 식 (6.1)로 재계산해도 8.314 × 273.15 / 101325 ≈ 0.0224 m³). 그보다 더운 25 °C에서 2.05 L는 10배 이상 어긋난다.
- 극한값: 이 코드에 T = 0(섭씨 0도)을 넣으면 부피가 정확히 0, 음수 섭씨를 넣으면 음수 부피가 나온다. 절대온도 미변환의 전형적 신호다.
분석 판정문: "8행에서 섭씨 25를 절대온도 변환 없이 식 (6.1)의 T에 대입했다. 식 (6.2)로 298.15 K를 만들어 대입해야 하며, 올바른 결과는 약 24.5 L다." 이 오답은 문법 오류도 런타임 오류도 아닌 순수한 의미 오류였고, 검증 루틴 세 개 모두에 걸렸다. 하나만 성실히 돌렸어도 잡혔다는 뜻이다. 이 예제에서 얻을 교훈은 두 가지다. 프롬프트에 단위를 썼다고 AI가 단위를 지키는 것은 아니라는 것, 그리고 극한값 체크는 실행 없이 머리로도 돌릴 수 있다는 것이다. 교정 코드를 받은 뒤 T에 −10 °C를 넣어 재실행해 보라. 교정 전 코드라면 음수 부피가 나왔을 입력이다.
채점이 끝났으면 교정을 요구할 차례다. 원인을 이미 알고 있으므로 교정 프롬프트는 구체적으로 쓸 수 있다. "고쳐줘" 대신 무엇을 어떻게 고칠지를 지정한다.
이 코드는 섭씨 온도를 절대온도로 변환하지 않고 기체상수 8.314 J/(mol·K)와 곱하고 있어. 함수가 섭씨를 입력받아 내부에서 273.15를 더해 켈빈으로 바꾸도록 고치고, 모든 변수에 단위 주석을 달아 줘. 절대온도가 0 이하가 되는 입력은 예외를 던지게 해.
R = 8.314 # 기체상수 [J/(mol·K)], 출처: NIST CODATA
def ideal_gas_volume(n, T_celsius, P_pa):
"""이상기체 부피 [m^3]. 입력: n[mol], T[°C], P[Pa]"""
T_kelvin = T_celsius + 273.15 # 절대온도 변환 [K]
if T_kelvin <= 0:
raise ValueError("절대온도는 0 K보다 커야 한다")
return n * R * T_kelvin / P_pa
v = ideal_gas_volume(1.0, 25.0, 101325.0) # 25 °C, 1 atm
print(f"부피: {v * 1000:.1f} L")
✔ 실행 결과: 부피: 24.5 L. 대조값을 재확인하면, 같은 함수에 0 °C를 넣으면 22.4 L가 나와 일반화학 상식과 일치한다. 극한값을 재확인하면, −300 °C를 넣으면 ValueError가 나며 음수 부피는 더 이상 만들어지지 않는다. 매개변수 이름 자체(T_celsius, P_pa)에 단위가 들어가 다음 사람의 실수도 막는다.
생각해보기 이번 오답은 대조값과 10배 이상 어긋나서 쉽게 걸렸다. 만약 틀린 값이 그럴듯한 범위 안에 있다면(예: 참값 24.5 L에 대해 26 L) 무엇으로 잡을 수 있는가? 방법을 두 가지 제안하라.
6.4 디버깅 프롬프트 전략: 가설이 먼저다
채점으로 원인을 찾았다면 다음은 고치는 일이다. 이 일을 디버깅(debugging)이라 한다. 검증된 프로그래밍 교재들은 디버깅을 네 가지 활동으로 정리한다. 읽기(코드를 다시 읽고 의도와 대조), 실행(바꿔 가며 돌려 보기), 숙고(증상에서 원인을 추리), 후퇴(최근 변경을 되돌려 작동하던 버전으로 복귀)이다. 초보자의 흔한 함정은 이 중 하나에만 매달리는 것이다.
디버깅은 실험 과학처럼 진행해야 한다. 원인에 대한 가설을 최소 하나 세우고, 가설이 맞다면/틀리다면 무엇이 관찰될지를 정한 뒤, 그것을 확인하는 실험(실행)을 설계한다. 가설 없이 코드를 무작위로 바꿔 가며 되기를 비는 방식을 무작위 행보 프로그래밍(random walk programming)이라 부르는데, 오래 걸리기로 악명이 높다.
AI 시대의 무작위 행보는 "고쳐줘"의 무한 반복이다. AI는 매번 그럴듯한 수정본을 내놓기 때문에 진전처럼 느껴지지만, 원인 진단이 없으므로 수렴이 보장되지 않고 코드는 반복 수정으로 점점 읽기 어려워진다. 나쁜 예부터 지면에 올린다.
안 돼. 아직도 값이 이상해. 그냥 고쳐줘.
(AI가 함수 구조를 통째로 바꾼 수정본을 제시. 무엇이 왜 바뀌었는지 설명 없음, 발췌 생략)
✔ 게재 이유: 이렇게 하지 말라는 견본이다. 증상 정보가 0이므로 AI는 추측으로 고칠 수밖에 없고, 진단 없는 수정은 검증할 기준도 없다. 어디가 바뀌었는지 모르는 코드는 채점 절차를 처음부터 다시 돌아야 한다.
좋은 디버깅 프롬프트는 세 패턴으로 요약된다. 패턴 1은 진단 요구다. 에러 메시지 전문과 코드를 주되, 수리를 시키지 않고 진단만 시킨다. "고치지 말고, 원인 후보 3개와 각각을 확인할 방법을 말하라." 후보를 받으면 확인 실험은 직접 한다. 원인을 확정하는 주체가 자신이어야 다음 단계의 교정 프롬프트를 구체적으로 쓸 수 있다.
패턴 2는 기대–실제 병기다. 의미 오류에는 에러 메시지가 없으므로 증상을 수치로 만들어 줘야 한다. "기대값은 약 24.5 L(근거: 0 °C에서 22.4 L), 실제 출력은 2.05 L로 약 12배 작다. 이 배율 차이를 만들 수 있는 원인부터 짚어라." 기대와 실제의 차이 크기 자체가 강력한 진단 단서다.
패턴 3은 역할 전환이다. 문제를 남에게 소리 내어 설명하다가 스스로 답을 찾는 기법을 고무 오리 디버깅(rubber duck debugging)이라 한다. AI와 함께라면 방향을 뒤집을 수 있다. "이 코드를 한 줄씩, 각 줄에서 변수의 단위가 무엇인지와 함께 설명하라." 설명을 읽는 쪽이 되면 자신이 쓴 프롬프트의 가정과 코드의 실제 동작 사이의 틈이 보인다. 예제 6.3-1의 코드에 이 프롬프트를 걸면 "T에 25가 들어온다"는 설명 줄에서 단위가 비어 있음이 드러난다.
세 패턴 모두에 통하는 보조 기술이 하나 있다. 문제를 일으키는 가장 작은 코드와 입력으로 줄여서 묻는 것이다. 이렇게 줄인 사례를 최소 재현 예제(minimal reproducible example)라 한다. 500행짜리 파일 전체 대신 예외를 일으키는 함수 하나와 입력 한 줄로 줄이면, 줄이는 과정에서 원인이 먼저 드러나는 일도 흔하다.
마지막으로 경계할 것은, AI의 교정이 원인 제거가 아니라 증상 은폐일 수 있다는 점이다. 교정 코드를 받으면 반드시 차이를 읽고 "원인을 없앴는가, 신호를 껐는가"를 물어야 한다.
스스로 점검
- "고쳐줘" 반복이 무작위 행보 프로그래밍과 같은 이유를 한 문장으로 설명하라.
- 의미 오류를 AI에게 보고할 때 에러 메시지 대신 무엇을 제공해야 하는가?
6.5 시험1 안내: AI 없이 남는 것을 잰다
다음 주 시험1은 20점, 오프라인 지필이며 AI는 전면 금지다. 범위는 1~6주 누적이다. 최종 시험이 AI를 필수로 쓰는 실기인데 왜 첫 시험은 정반대인가. 최종 시험에서 AI를 전면 사용하기 때문이다. AI 없이 남는 개인의 두뇌, 곧 코드 읽기·검증·의사코드 설계 능력은 반대 조건에서만 따로 잴 수 있다. 이 수업의 AI 활용 규칙(시험1 금지, 시험2·최종 시험 필수)은 이 논리 위에 서 있다.
문항 유형은 네 가지다. ① 생성 코드 출력 예측 ② EiPE(제시된 AI 생성 코드를 한국어로 목적·동작 설명) ③ "AI가 낸 오답을 채점·교정하라"(단위 오류·날조 물성 포함) ④ 화공 계산문제의 의사코드 설계이다. 각 유형은 이미 이 책 어딘가에서 훈련했다. 표 6-3이 그 지도다.
| 문항 유형 | 훈련한 곳 | 이번 주 점검 방법 |
|---|---|---|
| ① 생성 코드 출력 예측 | 5장 코드 해부, 각 장 스스로 점검 | 모의 시험 문항 1 |
| ② EiPE 설명 | 5장 EiPE, 5주차부터 구술 | 모의 시험 문항 2 |
| ③ 오답 채점·교정 | 이 장 §6.3 채점 4단 | 모의 시험 문항 3 |
| ④ 의사코드 설계 | 2장 문제 분해, 예제의 풀이 전략 단 | 모의 시험 문항 4 |
대비 전략은 별것이 아니다. 이번 주 실습의 모의 시험을 실전 조건 그대로 치르고, 틀린 문항의 원인을 분류해 남은 일주일의 복습 우선순위를 정하는 것이다. 그 절차가 아래 실습이다.
실습 6: 모의 시험1 지필 리허설과 오답 리뷰 (무채점)
예상 소요: 모의 시험 60분 + 자가 채점·오답 리뷰 45분 + 위키 기록 15분. 타이머를 사용한다. 결과는 채점되지 않는다. 이 실습의 산출물은 점수가 아니라 자가 진단표다. 진단표가 정직할수록 남은 일주일의 복습 계획이 정확해진다.
준비물
필기구와 답안지(A4). 시험 단계에서는 노트북을 덮는다. 시험1과 동일하게 AI는 전면 금지다. 문항지는 실습 시작 시 배포한다(아래 견본과 같은 형식, 유형 ①~④ 각 1문항). 오답 리뷰 단계부터는 노트북과 AI를 다시 연다. 5장까지의 본인 위키가 최신 상태인지 확인해 두면 리뷰가 빨라진다.
과제
- 실습 6.1. 60분 타이머를 걸고 4문항을 지필로 풀어라. (완료 기준: 4문항 모두 답안 작성. 모르는 문항도 아는 데까지 쓴다)
- 실습 6.2. 권말 해답과 대조해 스스로 채점하라. (완료 기준: 문항별 정오 표시와 부분 점수 메모)
- 실습 6.3. 틀린 문항마다 원인을 세 갈래 중 하나로 분류해 자가 진단표를 작성하라: ⑴ 지식 공백(개념을 몰랐다) ⑵ 코드 읽기 실수(알지만 잘못 읽었다) ⑶ 검증 생략(확인 절차를 건너뛰었다). (완료 기준: 표에 빈칸 없음)
- 실습 6.4. 틀린 문항 1개를 골라 §6.4의 패턴 1 또는 3으로 AI와 재분석하라. 이 단계부터 AI를 허용한다. (완료 기준: 진단 프롬프트와 AI 응답을 저장)
- 실습 6.5. 자가 진단표와 재분석 로그를 repo에 커밋하라. (완료 기준: 커밋 메시지에 "모의시험1" 포함)
견본 문항 (유형 ①~④, 실제 문항지는 이 형식을 따른다)
문항 1 (출력 예측). 다음 코드의 출력을 예측하라.
T = 25.0 # °C
T += 273.15 # K로 변환
print(int(T))
문항 2 (EiPE). 다음 생성 코드의 목적과 동작을 한국어 2~3문장으로 설명하라.
def filter_temps(temps, t_min, t_max):
ok = []
for t in temps:
if t_min <= t <= t_max:
ok.append(t)
return ok
문항 3 (오답 채점·교정). AI가 "kPa 압력을 Pa로 바꾸는 함수"로 아래를 제시했다. 채점 4단 절차로 채점하고 판정문을 써라.
def kpa_to_pa(p_kpa):
return p_kpa / 1000
문항 4 (의사코드 설계). 물질명과 온도를 입력받아, 그 물질의 정상 끓는점을 조회한 뒤 주어진 온도에서 액체인지 기체인지 판정하는 프로그램의 의사코드를 6줄 이내로 써라. (물성 조회는 5주차 도구④의 함수를 쓴다고 가정)
완성 기준
- 4문항 답안 작성 완료 (60분 내, 지필)
- 자가 진단표 완성. 틀린 문항 전부에 원인 분류 ⑴~⑶ 기입
- AI 재분석 로그 1건 (진단 프롬프트 + 응답 + 본인 결론 1문장)
- repo 커밋 1건 이상
통과하지 못한 항목은 다음 실습 전까지 재도전한다.
막혔는가?
아래 힌트는 전부 오답 리뷰 단계(AI 허용) 전용이다. 시험 단계에서는 쓸 수 없다.
- 채점 근거가 안 써지면 이렇게 물어라. "이 코드의 각 줄에 변수의 단위를 주석으로 달아 줘. 등호 양변의 단위가 안 맞는 줄이 있으면 지목해 줘."
- 원인 분류가 애매하면 스스로에게 물어라. 시험장에서 그 개념의 정의를 쓸 수 있었는가(아니면 ⑴), 코드를 손으로 한 줄씩 따라가 봤는가(안 했으면 ⑵), 답을 쓰고 나서 말이 되는지 확인했는가(안 했으면 ⑶).
- 재분석이 막히면 이렇게 물어라. "고치지 말고, 내 답이 틀린 원인 후보 3개와 각각을 확인할 방법을 말해 줘. 내 답은 [본인 답], 정답은 [해답]이야."
퇴실 전 기록 오늘의 자가 진단표에서 가장 아픈 오답 1건을 위키에 기록하고 커밋해야 퇴실한다. 문항, 본인의 오답, 원인 분류, 그리고 "시험1에서 같은 유형을 만나면 무엇을 먼저 할 것인가" 한 문장을 담는다.
요약
- S 6-1 (트레이스백 읽기 순서) 아래에서 위로: 마지막 줄에서 예외 이름과 메시지(병명) → 바로 위 블록에서 파일·줄 번호(위치) → 더 위에서 호출 경로.
- S 6-2 (채점 4단 절차) ① 단위 손 추적 → ② 대조값 1점 → ③ 극한값 대입 → ④ 판정문(틀린 줄·원인·교정 방향).
- S 6-3 (디버깅 프롬프트 3패턴) 진단 요구("고치지 말고 원인 후보 3개") · 기대–실제 병기 · 역할 전환(한 줄씩 단위와 함께 설명시키기).
- 식 (6.1) PV = nRT, 식 (6.2) T[K] = T[°C] + 273.15. 이 장의 오답 채점에 쓴 두 식이다.
| 오류 유형 | 파이썬이 잡는가 | 사람의 탐지 도구 | 시험1 연결 |
|---|---|---|---|
| 문법 오류 | 실행 전에 잡는다 | 에러 위치 표시 읽기 | — |
| 런타임 오류 | 실행 중에 잡는다 | 트레이스백 (S 6-1) | 유형 ①·③ |
| 의미 오류 | 잡지 못한다 | 검증 루틴 3종 = 채점 4단 (S 6-2) | 유형 ③ |
5장이 AI 협업 사이클의 ④ 생성 코드 읽기를 열었다면, 이 장은 ⑤ 테스트와 ⑥ 화공 검증을 채점자의 수준으로 끌어올렸다. 방패였던 검증 루틴이 채점 기준표가 됐고, 에러 메시지는 두려움의 대상에서 진단 보고서로 바뀌었다. 도구 사다리는 잠시 멈춰 있지만 헛도는 주가 아니다. 시험1을 통과한 읽기·검증 능력이 8주차부터의 화공 도구 제작 전체를 떠받친다.
용어 정리
- 문법 오류 (syntax error)
- 파이썬 문법 규칙 위반으로 실행이 시작되지 못하는 오류.
- 런타임 오류 (runtime error)
- 실행 도중 더 진행할 수 없어 프로그램이 멈추는 오류.
- 의미 오류 (semantic error)
- 프로그램이 끝까지 돌지만 의도와 다른 일을 하는 오류. 파이썬이 잡아 주지 않는다.
- 예외 (exception)
- 런타임 오류 발생 시 파이썬이 일으키는 신호. 이름(NameError 등)이 원인의 범주를 알려 준다.
- 트레이스백 (traceback)
- 예외 발생 시 출력되는 보고서. 예외 이름, 발생 위치, 호출 경로를 담으며 아래에서 위로 읽는다.
- 고무 오리 디버깅 (rubber duck debugging)
- 문제를 소리 내어 설명하는 과정에서 스스로 원인을 찾는 기법. AI에게는 역할을 뒤집어 코드를 설명시킨다.
- 최소 재현 예제 (minimal reproducible example)
- 문제를 일으키는 가장 작은 코드와 입력으로 줄인 사례. 질문의 기본 단위.
연습문제
Q군: 개념·읽기 (AI 없이 풀어라. 시험1 대비)
- Q6.1 다음 코드의 출력을 예측하라. 예측 후에만 실행해 확인하라.
p_atm = 2.0 # atm p_kpa = p_atm * 101.325 # kPa print(int(p_kpa)) - Q6.2 다음 트레이스백을 읽고 (a) 예외 이름 (b) 발생 파일과 줄 번호 (c) 원인 1문장 (d) 교정 방향 1문장을 써라.
Traceback (most recent call last): File "mole_frac.py", line 9, in <module> y = mole_fraction(0.0, 0.0) File "mole_frac.py", line 5, in mole_fraction return n_a / (n_a + n_b) ZeroDivisionError: float division by zero - Q6.3 AI가 "g/cm³ 밀도를 kg/m³로 바꾸는 함수"로 아래를 제시했다. 채점 4단 절차로 채점하라. 대조값으로 물(1.0 g/cm³ = 1000 kg/m³)을 써라.
def density_si(rho_gcm3): """g/cm^3 -> kg/m^3""" return rho_gcm3 / 1000
P군: 제작·계산 (AI 사용 전제. 시험2·최종 시험 대비)
- P6.1 다음 코드를 실행해 트레이스백 전문을 확보한 뒤, §6.4 패턴 1(진단 요구)로 AI에게 원인 후보 3개를 받아라. 후보 각각을 직접 실험으로 확인하고, 확정 원인과 교정 코드를 README에 기록하라.
temps = ['25.0', '30.0', '35.0'] # °C, CSV에서 읽은 문자열 avg = sum(temps) / len(temps) print(avg + 273.15) - P6.2 예제 6.3-1의 교정 코드(코드 6-3)를 확장하라. 음수 압력 입력을
ValueError로 막고, 0 °C·25 °C·100 °C 세 점의 결과를 검증 루틴 3종과 함께 README에 기록하라. 커밋 2회 이상으로 나눠 제출하라. - P6.3 (도전·메타인지) 검증 루틴 3종(단위 체크, 대조값 1점, 극한값)을 모두 통과하면서도 여전히 틀린 계산 코드를 일부러 설계할 수 있는가? 시도해 보고, 성공했다면 그 오답을 잡아낼 네 번째 검증을 제안하라. 실패했다면 왜 어려운지를 3종 루틴의 역할 분담으로 설명하라.
이번 주 위키 기록 과제 ① 이 장 핵심 1페이지 정리(#seed): 오류 3유형 표와 채점 4단 절차를 자신의 말로. ② "내가 틀렸던 것" 1건: 모의 시험 오답 1건을 원인 분류와 함께(실습 6의 기록을 다듬어도 된다). ③ 타 과목 연결 1건: 화공양론 과제 검산에서 단위 손 추적을 적용한 사례. 이번 주 의미 커밋 1건 이상이 채점 지표다.
7장 중간 점검과 시험1
이번 주의 도구: 없음. 시험대에 오르는 것은 도구를 다루는 머리다
다음 상황을 생각해 보자. 지난 6주 동안 매주 실습실에서 노트북을 펴고 AI와 함께 도구를 만들던 학생이, 이번 주에는 연필과 답안지만 놓인 책상 앞에 앉아 있다. 첫 문항에는 처음 보는 코드가 인쇄되어 있다. 예전 같으면 그대로 실행해 보거나 AI에게 설명을 시켰겠지만 오늘은 둘 다 없다. 그런데 코드를 한 줄씩 짚어 내려가다 보면 낯익다. 5주차에 물성 조회기를 만들며 해부하던 반복문이고, 3주차에 AI의 단위 실수를 잡아내던 바로 그 패턴이다. 시험1은 새로운 내용을 묻지 않는다. 매주 실습에서 하던 읽기와 검증을, 도와줄 AI 없이 종이 위에서 재연하라고 요구할 뿐이다.
이 장에서 다루는 것
- 1~6장의 개념·도구·역량을 한 장의 지도로 정리한다
- 시험1의 규정과 "AI 금지" 설계 논리를 확인한다
- 문항 4유형(출력 예측 · EiPE · AI 오답 채점 · 의사코드 설계)을 예시 문항과 모범 답안으로 해부한다
- AI 없는 지필 시험에서 통하는 준비법과 답안 전략을 정리한다
학습목표 이 장을 마치면 다음을 할 수 있다.
- 1~6장의 핵심 개념과 도구를 역량 지도 위에 배치할 수 있다.
- 시험1의 규정과 AI 금지 설계의 논리를 설명할 수 있다.
- 생성 코드의 출력을 트레이스 표로 손으로 예측할 수 있다.
- AI가 낸 틀린 화공 계산을 검증 루틴 3종으로 채점하고 교정할 수 있다.
- 화공 계산문제의 풀이 절차를 검증 단계를 포함한 의사코드로 설계할 수 있다.
이번 주 실습 2시간은 시험1(20점, AI 전면 금지, 오프라인 지필) 그 자체다. 매주 실습이 최종 시험의 축소판이었다면, 시험1은 그 반대편, 곧 AI를 걷어냈을 때 남는 능력을 검증하는 자리다.
7.1 여섯 장의 지도: 무엇이 쌓였는가
1장에서 정한 역할 분담을 떠올려 보자. 코드는 AI가 쓰고, 학생은 문제 분해와 생성 코드 읽기, 그리고 화학공학적 검증을 책임진다. 2장은 이 분담을 실행하는 순서, 즉 문제 분해→프롬프트→코드 읽기→테스트 사이클을 몸에 익혔고 그 첫 산출물이 도구① 강의노트 정리기였다. 3장은 그 반대편을 보였다. AI가 틀리는 방식(환각·단위 오류·물성 날조)을 실증하고, 검증 루틴 3종(단위 체크 · 대조값 · 극한값 sanity)을 도입했다.
4장은 간격반복과 인출연습이라는 학습과학 위에 도구③ 스터디 플래너를 얹어 학습 OS를 완성했다. 5장에서는 첫 화공 도구인 도구④ 단위환산 + 물성 조회기를 만들며, 생성 코드를 변수·제어문·함수 단위로 해부해 말로 설명하는 EiPE 훈련이 시작됐다. 6장은 그 읽기 능력을 채점자의 자리로 옮겼다. AI가 낸 틀린 화공 계산을 채점·교정했고, 모의 시험1로 이번 주를 리허설했다.
표 7-1은 여섯 장을 한 장으로 접은 지도다. 각 장이 남긴 개념과 도구, 그것이 기른 역량을 시험1의 문항 유형과 연결했다. 오른쪽 끝 열이 이 장의 주제다.
| 장 | 남긴 개념 | 만든 것 | 기른 역량 | 시험1 연결 |
|---|---|---|---|---|
| 1장 | 역할 정의, AI 활용 규칙 | 개발환경 + 위키 repo | Git 규율(C3), 학습 OS(C7) | 전 유형의 전제 |
| 2장 | 분해→프롬프트→읽기→테스트 사이클, 마크다운·Git 규율 | 도구① 강의노트 정리기 | 문제 분해(C1), Git 규율(C3) | 유형 ④ |
| 3장 | 환각·단위 오류·물성 날조, 검증 루틴 3종 | 도구② 플래시카드 생성기 | 검증(C5) | 유형 ③ |
| 4장 | 간격반복·인출연습, 위키 구조화 | 도구③ 스터디 플래너 | 학습 OS(C7) | 응시 전략(7.4절) |
| 5장 | 변수·제어문·함수 해부, EiPE | 도구④ 단위환산 + 물성 조회기 | 코드 읽기(C2), 계산 가능화(C4) | 유형 ①·② |
| 6장 | AI 오답 채점·교정 | 모의 시험1 자가 진단표 | 코드 읽기(C2), 검증(C5) | 유형 ③ 직결 |
C1~C7은 이 수업이 역산 설계한 7대 역량의 번호다.
같은 지도를 AI 협업 사이클 위에 겹쳐 보면 시험1의 성격이 더 분명해진다. ③ 프롬프트와 ⑤ 테스트는 AI와 컴퓨터가 있어야 돌아가는 블록이므로 시험2와 최종 시험에서 검증한다. 시험1이 겨누는 것은 나머지 세 블록인 ② 분해 · ④ 생성 코드 읽기 · ⑥ 화공 검증이다.
- 문제 정의
- 분해
- 프롬프트
- 생성 코드 읽기
- 테스트
- 화공 검증
스스로 점검
- 도구①~④ 중 위키를 직접 읽고 쓰는 도구는 어느 것인가?
- 검증 루틴 3종이 처음 도입된 장은 어디이고, 세 항목은 무엇인가?
7.2 시험1: 규정과 AI 금지의 논리
시험1은 20점이며, 7주차 실습 시간에 오프라인 지필로 치르고 AI 사용은 전면 금지된다. 범위는 1~6주 누적이다. 문항은 네 유형으로 고정되어 있다. ① 생성 코드 출력 예측, ② EiPE(제시된 AI 생성 코드를 한국어로 목적·동작 설명), ③ AI가 낸 오답을 채점·교정하라(단위 오류·날조 물성 포함), ④ 화공 계산문제의 의사코드 설계. 모든 평가물에 AI 사용내역을 첨부하는 1장의 의무는 시험1에도 그대로 적용된다.
AI 활용 규칙 세 종류 가운데 시험1은 유일한 금지에 해당한다. 시험2(20점, 12주, 90분 현장 실기)와 최종 시험(40점, 2시간 바이브코딩 + GitHub + 구술 방어)은 AI가 필수이고, 과제는 허용이다. 금지 종류가 존재하는 이유는 역설적으로 최종 시험에서 AI를 전면 사용하기 때문이다. AI가 코드를 다 써 주는 시험장에서 중요해지는 것은 생성된 코드를 읽어 내고, 결과가 물리적으로 말이 되는지 판정하고, 문제를 계산 가능한 절차로 쪼개는 개인의 능력이다. 시험1은 그 능력이 실제로 남아 있는지를, AI를 걷어낸 상태에서 따로 검증한다.
낯선 형식은 아니다. 6주차 실습에서 모의 시험1을 무채점으로 치렀고, 오답 리뷰로 자가 진단표를 만들었다. 이 장의 7.3절은 그 진단표 위에 올릴 유형별 처방이다.
| 유형 | 무엇을 묻는가 | 훈련한 곳 | 채점 초점 |
|---|---|---|---|
| ① 출력 예측 | 코드를 손으로 실행하는 능력 | 5장, 매주 실습 | 출력 전체를 순서·형식까지 정확히 |
| ② EiPE | 코드의 목적을 요약하는 능력 | 5장, 5주차부터의 구술 | 목적 문장 + 입출력과 단위 |
| ③ 오답 채점·교정 | 틀린 계산을 검증으로 잡는 능력 | 3장·6장 | 오류 위치 · 근거 · 교정값 3요소 |
| ④ 의사코드 설계 | 문제를 절차로 분해하는 능력 | 2장, 매주 분해 연습 | 입력→처리→출력→검증의 완전성 |
생각해보기 시험1이 없다고 가정해 보자. 어떤 학생이 유리해지고, 15주가 끝났을 때 무엇이 검증되지 않은 채 남는가? 반대로 세 시험이 전부 AI 금지라면 이 수업의 무엇이 무너지는가?
7.3 문항 4유형 해부
네 유형을 하나씩 해부한다. 유형마다 실제 시험과 같은 형식의 예시 문항 하나와 모범 답안을 실었다. 예시는 전부 1~6주 범위의 코드와 화공 지식만 사용한다.
7.3.1 유형 ①: 생성 코드 출력 예측
출력 예측은 컴퓨터 없이 스스로 컴퓨터가 되어 보는 문항이다. 인터프리터의 규칙은 단순하다. 코드는 위에서 아래로 한 줄씩 처리되고, 함수가 호출되면 함수 몸통으로 들어갔다가 return에서 호출한 자리로 돌아온다. 이 규칙을 종이 위에서 그대로 따라가면 된다.
예시 문항. 다음 코드가 인쇄하는 출력 전체를 순서대로 쓰라.
def judge(err_pct): # err_pct: 검증 오차 [%]
if err_pct < 1:
return "PASS"
elif err_pct < 5:
return "RECHECK"
else:
return "FAIL"
errs = [0, 1, 5, 3] # 도구 4개의 검증 오차 [%]
n_pass = 0
for e in errs:
r = judge(e)
if r == "PASS":
n_pass = n_pass + 1
print(e, r)
print("passed:", n_pass)
머릿속 실행은 두 번째 반복쯤에서 흔들리기 쉽다. 코드를 한 줄씩 실행한 셈 치고 각 변수의 값 변화를 기록한 표를 트레이스 표(trace table)라 한다. 답안지 여백에 이렇게 그린다.
e | judge(e)의 분기 | r | n_pass | 인쇄 |
|---|---|---|---|---|
| 0 | 0 < 1 참 | "PASS" | 1 | 0 PASS |
| 1 | 1 < 1 거짓 → 1 < 5 참 | "RECHECK" | 1 | 1 RECHECK |
| 5 | 5 < 5 거짓 → else | "FAIL" | 1 | 5 FAIL |
| 3 | 3 < 1 거짓 → 3 < 5 참 | "RECHECK" | 1 | 3 RECHECK |
모범 답안. 출력은 다음 다섯 줄이다.
0 PASS
1 RECHECK
5 FAIL
3 RECHECK
passed: 1
함정은 경계값이다. e = 1에서 1 < 1은 거짓이므로 첫 분기를 통과하지 못하고 elif로 내려간다. e = 5도 같은 이유로 else까지 밀린다. 부등호가 <인지 <=인지를 확인하는 습관이 이 유형에서 큰 비중을 차지한다. 나머지는 출력 형식이다. print(e, r)은 두 값을 공백으로 잇고, 마지막 줄은 passed: 1처럼 문자열 뒤에 값이 붙는다.
7.3.2 유형 ②: EiPE, 코드를 말로 설명하기
코드를 말로 설명하기(Explain in Plain English, EiPE)는 코드를 몇 줄씩 번역하는 문항이 아니다. 코드 전체가 무엇을 하는 물건인지 한 단계 위에서 요약하는 문항이다. 5장에서 좋은 설명과 나쁜 설명의 차이를 봤다. 시험에서도 그 차이가 점수로 이어진다.
예시 문항. 다음 함수의 목적과 동작을 한국어 2~3문장으로 설명하라. 입력과 출력의 단위를 명시하라.
def mole_fractions(masses, mws):
# masses: 성분별 질량 [g], mws: 성분별 몰질량 [g/mol]
moles = []
for i in range(len(masses)):
moles.append(masses[i] / mws[i]) # [mol]
total = sum(moles) # [mol]
fracs = []
for n in moles:
fracs.append(n / total) # 무차원
return fracs
모범 답안(만점). "성분별 질량 리스트[g]와 몰질량 리스트[g/mol]를 받아 각 성분의 몰수를 구한 뒤, 전체 몰수에 대한 비율인 몰분율 리스트를 돌려주는 함수다. 반환값은 무차원이고, 합은 1이 되어야 한다."
0점 답안(실제 유형). "빈 리스트를 만들고, for문으로 masses[i]를 mws[i]로 나눠 moles에 넣고, sum으로 total을 만들고, 다시 for문을 돌아 나눈 값을 fracs에 넣어 반환한다."
두 답안 모두 사실만 적었지만, 뒤의 답안에는 이 함수가 몰분율을 계산한다는 목적이 없다. 줄 번역은 코드를 다시 읽어 주는 일에 가까워 이해의 증거로는 부족하다. 채점 기준은 세 가지다. 목적의 명명(몰분율), 입출력과 단위, 그리고 결과의 성질(합 = 1) 언급이다.
7.3.3 유형 ③: AI 오답 채점·교정
예제 7.3-1 AI 오답 채점: 이상기체의 부피
문제 다음은 "25 °C, 100 kPa에서 이상기체 2.0 mol이 차지하는 부피를 구하라"는 문제에 AI가 낸 풀이다. 채점하고, 틀렸다면 오류의 위치와 근거를 지적한 뒤 올바른 값으로 교정하라.
이상기체 상태방정식 PV = nRT에서 V = nRT/P이다. R = 8.314 J/(mol·K)이므로 V = (2.0 × 8.314 × 25) / (100 × 10³) = 4.2 × 10⁻³ m³ ≈ 4.2 L이다.
풀이 전략 채점은 검증 루틴 3종의 순서대로 간다. 단위를 먼저 보고, 아는 값과 대조하고, 극한을 상상한다. 세 검사 중 하나라도 걸리면 그 지점이 오류의 좌표다.
채점과 교정 첫 번째 검사에서 즉시 걸린다. R = 8.314 J/(mol·K)의 온도는 켈빈인데, AI는 섭씨 25를 그대로 대입했다. 교정하면 T = 25 + 273.15 = 298.15 K이고,
V = (2.0 × 8.314 × 298.15) / (100 × 10³) = 0.0496 m³
답: V ≈ 49.6 L
- 단위: R = 8.314 J/(mol·K)이므로 T는 K 단위여야 하는데 AI는 °C 값 25를 대입 → 여기서 탈락
- 대조값: 표준상태(0 °C, 100 kPa)의 몰부피 22.7 L/mol(LibreTexts) → 2.0 mol이면 45.4 L, 25 °C는 이보다 조금 커야 함 → 49.6 L 정합, AI의 4.2 L은 10배 넘게 벗어남
- 극한값: AI의 식대로면 0 °C에서 V = 0이다. 섭씨 0도에서 기체 부피가 사라진다는 뜻이므로 물리적으로 불가하다
분석 AI 오답의 전형은 공식은 맞고 대입이 틀린 경우다. 식만 훑으면 통과시키기 쉽다. 채점의 단서는 대개 숫자와 단위에 있다. 답안 작성에도 규칙이 있다. "틀렸다"고만 쓰면 부분 점수다. 오류의 위치(°C를 그대로 대입), 근거(R의 온도 단위는 K), 교정값(49.6 L)의 3요소를 채워야 만점이다. What-if: 같은 풀이에서 압력을 100 kPa 대신 100 Pa로 잘못 읽으면 부피가 몇 배로 튀는지, 어느 검증 루틴이 그것을 잡는지 손으로 확인해 보라.
7.3.4 유형 ④: 화공 계산문제의 의사코드 설계
프로그래밍 언어의 문법 대신 사람이 읽는 자연어로 계산 절차를 단계별로 적은 것을 의사코드(pseudocode)라 한다. 유형 ④는 파이썬 문법을 묻지 않는다. 채점 대상은 절차의 완전성이다. 입력·처리·출력이 다 있는가, 예외와 검증이 설계 안에 들어 있는가.
예시 문항. 단위가 kPa, bar, atm으로 섞인 압력 측정값 목록을 받아 전부 kPa로 통일한 뒤, 최댓값과 그 위치를 보고하는 절차를 의사코드로 설계하라. 예외 처리와 검증 단계를 포함하라.
입력: (값, 단위) 쌍의 목록 P # 예: (95.2, "kPa"), (1.30, "bar")
환산표 F를 정의한다: kPa→1, bar→100, atm→101.325 # kPa 기준
빈 목록 P_kpa를 만든다
P의 각 (v, u)에 대해 반복:
u가 F에 없으면 → "미지 단위 u" 오류를 보고하고 중단 # 예외 처리
P_kpa에 v × F[u]를 추가한다 # [kPa]
P_kpa가 비어 있으면 → "데이터 없음"을 보고하고 중단 # 극한(빈 입력)
최댓값 p_max와 위치 i_max를 첫 항목으로 놓고 순회하며 갱신한다
검증: 1 atm 입력이 101.325 kPa로 환산되는지 확인한다 # 대조값
출력: p_max [kPa], i_max
골격은 2장에서 이미 배웠다. 입력 → 처리 → 출력, 그리고 이 수업에서는 검증이 네 번째 고정 단계다. 위 답안에서 점수를 만드는 줄은 세 줄의 방어다. 미지 단위 중단, 빈 목록 처리, 대조값 확인이 그것이다. 물질수지 문제를 풀 때 "마지막에 답을 점검하라"가 절차의 정식 단계이듯이, 의사코드에서도 검증은 하나의 정식 단계다.
스스로 점검
- 유형 ③에서 통째로 틀린 계산을 가장 빨리 걸러내는 검증 루틴은 보통 무엇인가?
- EiPE 답안에서 0점을 부르는 전형적인 서술 방식은 무엇인가?
7.4 응시 전략: AI 없이 지필에서 이기는 법
7.4.1 시험 전: 인출로 대비한다
4장에서 배운 대로 다시 읽기보다 인출이 효과적이다. 요약본을 훑는 것보다 스스로 답을 꺼내 보는 인출연습(retrieval practice)이 기억을 만든다. 마침 그 도구를 직접 만들어 두었다. 도구② 플래시카드 생성기로 1~6장 위키 노트를 덱으로 바꿔 돌려 보는 것이 실질적인 대비가 된다.
출발점은 모의 시험1의 자가 진단표다. 네 유형 중 어디에서 점수를 잃었는지가 복습 순서를 정한다. 약한 유형은 AI에게 변형 문제를 만들게 해서 보충할 수 있다. 시험장 밖의 대비는 AI 활용 규칙의 허용에 해당한다. 단, 아래 리스팅처럼 답은 받지 않는 것이 요령이다.
아래는 내가 모의 시험1에서 틀린 문항이다. [문항 붙여넣기] 같은 개념을 묻는 변형 문제를 3개 만들어 줘. 조건: 1~6주 범위(변수·조건문·반복문·함수·리스트)만 쓰고, 답과 해설은 주지 마.
변형 1. 다음 코드의 출력을 예측하시오. (온도 목록을 순회하며 조건 분기로 등급 문자열을 인쇄하는 12줄 코드) … 변형 2. … 변형 3. (중첩 딕셔너리를 순회하는 코드) …
✔ 변형 3이 중첩 딕셔너리를 사용해 요청한 1~6주 범위를 벗어나 제외하고 재요청했다. 남은 2문항은 직접 푼 뒤 파이썬으로 실행해 내 답과 대조했다.
7.4.2 시험장에서: 순서와 골격
답안지를 받으면 전 문항을 훑고 자신 있는 유형부터 처리한다. 막히는 문항은 표시하고 넘어간다. 네 유형은 서로 독립이라 순서를 바꿔도 잃는 것이 없다.
유형별 골격은 이미 7.3절에 있다. 출력 예측은 머릿속 실행을 믿지 말고 트레이스 표를 그린다. EiPE는 목적 한 문장을 먼저 쓴다. 목적이 서면 동작과 단위는 따라오고, 거꾸로 줄 번역부터 시작하면 목적 문장을 끝내 못 쓰는 답안이 된다. 채점·교정 문항은 검증 루틴의 순서 그대로 단위, 대조값, 극한값을 훑고, 하나 걸리면 오류 위치·근거·교정값의 3요소를 채운다. 의사코드는 입력→처리→출력→검증의 골격을 먼저 쓰고 살을 붙인다. 백지에서 첫 줄부터 완성하려 들면 시간이 부족해진다.
생각해보기 출력 예측 문항에 나올 수 없는 코드의 조건을 두 가지 제안해 보라. 힌트: 채점자에게는 유일한 정답이 필요하다.
요약
- S 7-1 검증 루틴 3종: 단위 체크 → 대조값 → 극한값 sanity. 유형 ③의 채점 순서 그 자체다.
- S 7-2 EiPE 답안 골격: 목적 한 문장 → 입출력과 단위 → 결과의 성질. 줄 번역은 0점이다.
- S 7-3 의사코드 골격: 입력 → 처리 → 출력 → 검증(+예외). 검증은 장식이 아니라 단계다.
| 구분 | 시험1 | 시험2 | 시험3 |
|---|---|---|---|
| 배점 | 20점 | 20점 | 40점 |
| 시기 | 7주 | 12주 | 15주 + 시험주간 |
| AI | 금지 | 필수 | 필수 |
| 형식 | 오프라인 지필 | 90분 현장 실기 | 2시간 바이브코딩 + GitHub + 구술 방어 |
| 검증하는 것 | AI 없이 남는 능력(읽기·검증·분해) | 시간 압박 하 도구 제작 | 종합 실기 + 구술 방어 |
1~4주의 학습 OS와 5~6주의 읽기·검증 능력이 시험1에서 한 번 확인된다. 사이클의 ②·④·⑥ 블록을 종이 위에서 증명하고 나면, 8장부터는 Antoine 증기압에서 공정 대시보드까지 도구 사다리를 오르는 일이 남는다. 그때부터 AI를 다시 옆에 두고 작업한다.
용어 정리
- 의사코드(pseudocode)
- 프로그래밍 언어의 문법 대신 자연어로 계산 절차를 단계별로 적은 표현. 채점 대상은 문법이 아니라 절차의 완전성이다.
- 트레이스 표(trace table)
- 코드를 한 줄씩 실행한 셈 치고 각 변수의 값 변화를 기록하는 표. 출력 예측 문항의 기본 도구다.
연습문제
Q군: 개념·읽기 (AI 없이 풀라)
- Q7.1 코드 7-1에서
errs = [4, 5, 0]으로 바꿨을 때 인쇄되는 출력 전체를 순서대로 쓰라. 트레이스 표를 그려서 풀라. - Q7.2 코드 7-2의 목적과 동작을 두 문장으로 설명하고, 반환 리스트의 합이 항상 1인지 판단해 근거를 쓰라. (부동소수점 오차는 무시한다.)
- Q7.3 출제자가 되어 보라. 유형 ③ 문항에 심을 만한 그럴듯한 오류를 두 가지 제시하고, 각각 검증 루틴 3종 중 어느 검사에 걸리도록 설계했는지 설명하라.
- Q7.4 AI가 예제 7.3-1과 같은 문제를 풀며 이번에는 온도를 298.15 K로 올바르게 쓰고 압력을 100 kPa 대신 100 Pa로 대입해 V ≈ 4.96 × 10⁴ L을 얻었다. 이 답이 검증 루틴 3종 중 어느 검사에 걸리는지 밝히고 교정하라.
- Q7.5 여러 성분의 (질량 [g], 몰질량 [g/mol]) 목록으로 혼합물의 평균 몰질량을 구하는 절차를, 예외 처리(몰질량이 0 이하인 항목)와 검증 단계를 포함한 의사코드로 설계하라.
P군: 제작 (AI 사용, 시험 이후에 풀라)
- P7.1 도구② 플래시카드 생성기에 "오답 카드" 입력 경로를 추가하라. 시험 오답 1건이 (문항 요약 / 내 답 / 올바른 답 / 걸린 검증 루틴)의 4필드 카드로 덱에 들어가야 한다. 완성 기준: 모의 시험1 오답 2건이 카드로 변환되어 기존 덱과 함께 조회된다.
위키 기록 과제
- 1~6장 지도(표 7-1)를 본인의 언어로 다시 그린 1페이지 정리를
#seed로 커밋하라. - "내가 틀렸던 것". 모의 시험1 또는 시험1에서 틀린 문항 1건을 기록하라: 문항 요약, 내 답, 원인(개념 구멍인지 실수인지), 걸렸어야 할 검증 루틴.
- 타 과목 연결. 화공양론 과제 문제 하나를 유형 ④ 형식의 의사코드로 풀어 노트에 링크하라.
이번 주에도 의미 있는 커밋 1건 이상이 채점 지표다.
8장 증기압과 Antoine 식
이번 주의 도구: 증기압 계산기
다음 상황을 생각해 보자. 시험1을 막 끝낸 학생이 화공양론 과제를 풀다가 100 °C에서 물의 증기압이 필요해졌고, AI에게 계산을 맡겼다. 돌아온 답은 0.76 bar. 그런데 일반화학에서 배운 대로라면 물은 1 atm(= 101.325 kPa)에서 100 °C에 끓으므로, 그 온도의 증기압은 1 bar 근처여야 한다.
코드를 아무리 읽어도 버그가 없다. 식도 맞고, 상수도 AI가 준 표 그대로다. 문제는 코드가 아니라 상수의 유효 온도범위였다. 이 25% 오차가 어디서 왔는지 해부하는 일이 이 장의 출발점이다.
- 증기압을 분자 수준의 직관으로 정의한다
- Clausius-Clapeyron 식에서 Antoine 식이 나온 맥락을 형태 수준에서 잇는다
- Antoine 상수의 4종 확인 규칙과 외삽의 위험을 실제 오차 수치로 보인다
- matplotlib로 단위 라벨과 대조점이 있는 증기압 곡선을 그린다
- 도구⑤ Antoine 증기압 계산기를 45분 mini practical로 만든다
이 장을 마치면 다음을 할 수 있다.
- 증기압과 끓음의 관계를 분자 수준에서 설명할 수 있다.
- Antoine 식으로 주어진 온도에서 순수 물질의 증기압을 계산할 수 있다.
- Antoine 상수 표에서 로그 밑·압력 단위·온도 단위·유효 온도범위 4종을 확인하고, 하나라도 빠진 표를 결함으로 지적할 수 있다.
- 유효범위 밖 외삽이 만드는 오차를 정량적으로 보이고 원인을 설명할 수 있다.
- matplotlib로 축 라벨·단위·대조점이 있는 증기압 곡선을 그릴 수 있다.
- 유효범위 검사가 내장된 증기압 계산기를 AI와 함께 만들고 검증할 수 있다.
이번 주 실습은 8.2~8.4절을 사용해 도구⑤ Antoine 증기압 계산기를 만든다(45분 타이머, 학번별 물질). 최종 시험의 주제 예시 ①(학번별 이성분계의 기포점/이슬점 계산기 + T-xy 작도, NIST 대조 검증 포함)이 바로 이 함수 위에 선다. 이번 실습의 형식은 그 시험의 축소판이다.
8.1 증기압: 액체에서 탈출하는 분자들
책상 위에 둔 컵의 물은 끓이지 않아도 며칠이면 줄어든다. 액체 속 분자들의 속도는 제각각이고, 표면 근처의 평균보다 빠른 분자는 이웃 분자가 붙잡는 인력을 이기고 기체로 빠져나간다. 온도가 오르면 이 빠른 분자의 비율이 늘어나므로 탈출도 잦아진다.
이번에는 컵 대신 밀폐 용기를 생각해 보자. 탈출한 분자는 용기 안에 갇혀 있다가 일부는 다시 액면으로 돌아온다. 시간이 지나면 나가는 속도와 돌아오는 속도가 같아지는 상태, 즉 동적 평형(dynamic equilibrium)에 도달한다. 이때 기체가 자신의 액체(또는 고체)와 공존하며 만드는 압력을 증기압(vapor pressure)이라 한다.
증기압은 물질과 온도에만 의존하고, 온도가 오르면 커진다. 물질마다 값이 다른 이유는 분자간 인력의 세기가 다르기 때문이다. 인력이 약한 물질일수록 같은 온도에서 더 많이 탈출하고, 증기압이 높다.
끓음은 이 그림의 특수한 경우다. 액체 내부에서 기포가 자라려면 기포 속 증기의 압력이 외부 압력을 밀어낼 만큼 커야 하므로, p*가 외부 압력과 같아지는 온도에서 액체 전체가 끓는다. 이 온도를 끓는점(boiling point)이라 하고, 외부 압력이 1 atm일 때의 끓는점을 정상 끓는점(normal boiling point)이라 한다. 20 °C의 물도 압력을 0.023 atm 아래로 낮추면 끓는다. 20 °C에서 물의 증기압이 그 값이기 때문이다.
화공 계산에서 증기압은 도처에 나타난다. 저장 탱크의 증발 손실, 용매 회수, 그리고 9장에서 만날 증류가 전부 증기압 곡선 위에서 움직인다. 이 곡선을 온도의 함수로 계산해 주는 것이 이번 주의 도구다.
생각해보기 높은 산에서는 물이 100 °C보다 낮은 온도에서 끓는다. 이 사실을 증기압 곡선 하나로 설명하는 방법을 두 가지 제안하라(하나는 말로, 하나는 곡선 위의 점과 선으로).
스스로 점검 (해답은 권말)
- 밀폐 용기 속 액체의 증발이 멈춘 것처럼 보일 때, 분자 수준에서는 무슨 일이 일어나고 있는가?
- 외부 압력이 0.5 atm이라면 물은 100 °C보다 높은 온도에서 끓는가, 낮은 온도에서 끓는가?
8.2 Clausius-Clapeyron에서 Antoine으로
증기압이 온도에 따라 어떻게 변하는지는 열역학이 답을 준다. 액체-기체 평형에 열역학 법칙을 적용하면 Clausius-Clapeyron 식이 나오고, 몇 가지 근사를 거친 적분형은 다음과 같다.
ln(p*₂/p*₁) = (ΔHvap/R)(1/T₁ − 1/T₂) (8.1)
유도는 3학년 열역학에서 만난다. 여기서는 형태에서 읽을 수 있는 것 하나만 가져가자. ln p*는 1/T에 대해 대략 직선이라는 점이다. 그래서 좁은 온도 구간의 증기압 데이터는 다음 꼴로 잘 맞춰진다.
ln p* = A − B/T (8.2)
이때 B는 물리 상수가 아니라 데이터를 맞추기 위한 곡선 맞춤 파라미터다. 실제 데이터는 넓은 구간에서 완전한 직선이 아니므로, 분모에 보정 상수 C를 하나 더 넣어 휘어짐을 흡수한 것이 Antoine 식(Antoine equation)이다.
log₁₀ p* = A − B/(T + C) (8.3)
식 (8.3)은 이론에서 유도된 법칙이 아니라 측정 데이터에 맞춘 경험식(empirical correlation)이다. 이 구별을 이 장 전체에서 계속 쓴다. 법칙은 적용 범위를 물리가 정하지만, 경험식의 적용 범위는 맞춤에 사용한 데이터가 정한다.
8.2.1 Antoine 상수를 읽는 법: 반드시 확인할 네 가지
상수 A, B, C는 물질마다, 그리고 온도 구간마다 다르다. 더 성가신 문제는 출처마다 표기 약속이 다르다는 점이다. NIST WebBook 자료를 정리한 LibreTexts 교재조차 "출처에 따라 온도 단위가 다르거나 log₁₀ 대신 ln을 쓰기도 한다"고 경고를 달아 둔다. 같은 물질의 상수라도 압력이 bar인 표와 mmHg인 표, 온도가 K인 표와 °C인 표가 섞여 돌아다닌다.
그래서 이 책은 Antoine 상수 표에 네 가지(로그 밑, 압력 단위, 온도 단위, 유효 온도범위)를 반드시 명기한다. 표 8-1은 NIST WebBook이 주는 물의 상수다. 같은 물인데 행이 여러 개인 이유가 보이는가? 각 행은 서로 다른 연구진이 서로 다른 온도 구간에서 측정한 데이터에 맞춘 결과이기 때문이다.
| 유효 온도범위 (K) | A | B | C | 원 데이터 |
|---|---|---|---|---|
| 273 ~ 303 | 5.40221 | 1838.675 | −31.737 | Bridgeman & Aldrich (1964) |
| 304 ~ 333 | 5.20389 | 1733.926 | −39.485 | Bridgeman & Aldrich (1964) |
| 334 ~ 363 | 5.0768 | 1659.793 | −45.854 | Bridgeman & Aldrich (1964) |
| 344 ~ 373 | 5.08354 | 1663.125 | −45.622 | Bridgeman & Aldrich (1964) |
| 379 ~ 573 | 3.55959 | 643.748 | −198.043 | Liu & Lindsay (1970) |
NIST 원본에는 이 밖에 두 행(293~343 K, 255.9~373 K)이 더 있다. 행끼리 범위가 겹치기도 하고, 뒤에서 보겠지만 어느 행도 덮지 않는 틈도 있다. 물성 데이터의 실제 모습이 이렇다. 교과서의 매끈한 표 한 줄보다는 출처와 범위가 제각각인 조각들의 모음에 가깝다.
예제 8.2-1 물의 증기압 계산과 NIST 대조
문제 표 8-1의 상수를 사용해 25 °C(298.15 K)와 373 K에서 물의 증기압을 bar 단위로 구하라. 결과를 NIST의 정상 끓는점 데이터(373.17 ± 0.04 K에서 1 atm)와 대조하라.
풀이 전략 역할을 나눈다. 계산 함수를 짜는 일은 AI에게 맡기고, 온도에 맞는 상수 행을 고르는 일과 결과 검증은 직접 한다. 298.15 K는 273~303 K 행 안에, 373 K는 344~373 K 행 안에 들어가므로 각각 그 행을 쓴다. 프롬프트에는 로그 밑과 입출력 단위를 명시한다(8.2.1절의 4종 확인 규칙을 프롬프트에도 그대로 싣는 셈이다).
Antoine 식 log10(p*) = A − B/(T+C)로 증기압을 계산하는 파이썬 함수를 만들어 줘. 입력 온도는 켈빈, 출력은 bar. 상수 A, B, C는 인자로 받게 하고, 주석은 한국어로 달아 줘.
def antoine_p_bar(T_K, A, B, C):
"""Antoine 식: log10(p*) = A - B/(T + C)
T_K : 온도 [K]
A, B, C : Antoine 상수 (log10, bar, K 기준 세트)
반환 : 증기압 p* [bar]
"""
return 10 ** (A - B / (T_K + C))
✔ 밑이 10인 지수(10 **)를 썼는지 확인했다. 프롬프트의 log10 요구와 일치한다. 입출력 단위가 docstring에 남아 있고, 매직 넘버가 없다. 다만 유효범위 검사가 없다. 이 결함은 8.3절에서 다룬다.
온도에 맞는 행을 골라 실행해 보자.
# 25 °C = 298.15 K → 273~303 K 행 (Bridgeman & Aldrich, NIST)
p_25 = antoine_p_bar(298.15, 5.40221, 1838.675, -31.737) # bar
# 373 K → 344~373 K 행
p_100 = antoine_p_bar(373.0, 5.08354, 1663.125, -45.622) # bar
print(f"25 °C: {p_25:.4f} bar / 373 K: {p_100:.3f} bar")
실행 결과는 다음과 같다.
25 °C: 0.0317 bar / 373 K: 1.008 bar
- ✔ 단위 체크: 입력 T [K], 출력 p* [bar]로 표 8-1의 상수 세트 정의와 일치한다. C는 T에 더해지므로 온도 단위(K)를 가진다.
- ✔ 대조값: NIST 정상 끓는점 373.17 K에서 p* = 1 atm = 1.013 bar여야 한다. 373 K 계산값은 1.008 bar이며 0.5% 이내로 일치한다.
- ✔ 극한값 sanity: 온도를 75 K 내리자 증기압이 1/30 이하로 급감했다. 8.1절의 분자 직관(저온일수록 탈출 급감)과 방향이 맞다.
분석 계산 자체는 한 줄이다. 이 예제의 노동은 전부 상수 행 선택과 검증에 있었고, 실제로 그 부분이 사람 몫이다. 상수 조회를 건너뛰고 "AI가 준 상수"로 바로 계산했다면 어떤 값이 와도 믿을 근거가 없다. What-if: 코드 8-2에서 373 K 계산의 상수만 379~573 K 행(A = 3.55959)으로 바꿔 다시 실행해 보라. 그 결과가 다음 절의 주인공이다.
스스로 점검 (해답은 권말)
- 식 (8.3)에서 상수 C의 단위는 무엇인가?
- 표 8-1에서 350 K는 334~363 K 행과 344~373 K 행이 동시에 덮는다. 두 행의 계산값이 조금 다르다면, 이 차이는 버그인가 데이터의 성질인가?
8.3 유효범위와 외삽: 25% 오차의 해부
예제 8.2-1의 What-if를 실행했다면 373 K에서 0.759 bar를 얻었을 것이다. 장 도입의 0.76 bar가 바로 이것이다. 같은 식, 같은 온도, 같은 NIST 출처의 상수인데 결과가 25% 어긋난다. 379~573 K용 상수를 그 범위 밖인 373 K에 썼기 때문이다.
유효범위 안쪽 온도에서 상관식을 쓰는 일을 내삽(interpolation), 범위 밖으로 벗어나 쓰는 일을 외삽(extrapolation)이라 한다. Antoine 식은 데이터에 맞춘 경험식이므로, 데이터가 없는 구간에서는 아무것도 보증하지 않는다. 범위에서 겨우 6 K 벗어난 373 K에서 이미 25%가 깨졌다. 더 멀리 가면 어떻게 되는가? 같은 행으로 298.15 K를 계산하면 0.0013 bar가 나온다. 올바른 행의 계산값 0.0317 bar의 1/23이다.
이 함정은 문헌에도 그대로 기록되어 있다. LibreTexts 교재는 379~573 K 행으로 100 °C의 물 증기압을 계산해 0.76 bar를 얻고는, 실측 1.013 bar와 25.1% 어긋남을 표로 보여 주며 원인을 유효범위 밖 외삽으로 짚는다. 같은 자료에서 200 °C(473 K, 범위 안)의 계산값 16.53 bar는 실측 15.55 bar와 6.3% 차이였다. 표 8-2에 이 해부 결과를 모았다.
| 계산 온도 | 범위 판정 | 계산 p* | 비교 기준 | 어긋남 |
|---|---|---|---|---|
| 373 K | 6 K 밖 (외삽) | 0.759 bar | 1.013 bar (1 atm에서 끓음) | −25% |
| 298.15 K | 81 K 밖 (외삽) | 0.0013 bar | 0.0317 bar (범위 내 행 계산값) | 1/23로 붕괴 |
| 473 K | 범위 안 (내삽) | 16.53 bar | 15.55 bar (실측) | +6.3% |
표의 마지막 행이 보여 주듯 범위 안이라고 오차가 0이 되지는 않는다. 상관식은 어디까지나 데이터의 근사이고, 범위 안에서는 오차가 몇 % 수준으로 관리되는 반면 범위 밖에서는 오차의 상한을 말하기 어려워진다.
표 8-1을 다시 보면 더 불편한 사실이 하나 있다. NIST가 주는 물의 행 일곱 개를 전부 모아도 373~379 K 구간을 덮는 행이 없다. 하필 정상 끓는점 373.17 K가 이 틈에 걸린다. 이런 경우의 정직한 태도는 "가장 가까운 행을 쓰되, 몇 K의 외삽이 있었고 오차가 커질 수 있음을 기록에 남긴다"이다. 예제 8.2-1에서 344~373 K 행으로 373 K를 계산한 것이 이 태도에 해당한다.
범위 검사를 함수 안에 넣어 두면 미래의 나와 팀원이 같은 함정에 빠지는 일을 코드가 막아 준다. AI에게 교정을 요구해 보자.
코드 8-1 함수에 유효 온도범위 검사를 추가해 줘. 범위 최소·최대를 인자로 받고, 범위 밖 온도가 들어오면 계산하지 말고 에러를 던지게. 에러 메시지에 입력 온도와 유효범위를 포함해 줘.
def antoine_p_bar(T_K, A, B, C, t_min, t_max):
"""유효범위 검사가 있는 Antoine 계산.
T_K [K] 입력, p* [bar] 반환. t_min~t_max [K] = 상수의 유효범위."""
if not (t_min <= T_K <= t_max): # 범위 밖 = 외삽 → 거부
raise ValueError(
f"T = {T_K} K는 유효범위 {t_min}~{t_max} K 밖이다. "
"다른 상수 행을 찾거나 출처를 다시 확인하라.")
return 10 ** (A - B / (T_K + C))
✔ 범위 밖에서 값을 돌려주지 않고 ValueError를 던지는지 확인했다. 379~573 K 행에 373 K를 넣자 에러 메시지에 입력 온도와 범위가 그대로 찍혔다. 경계값 T = t_min, t_max는 통과된다(등호 포함). 의도와 일치한다.
생각해보기 범위 밖 온도가 들어왔을 때 계산기가 취할 수 있는 설계는 적어도 세 가지다. ① 에러를 던지고 멈춘다 ② 경고만 출력하고 값은 돌려준다 ③ 말없이 가장 가까운 범위의 상수로 바꿔 계산한다. 각 설계가 어떤 사용자에게 어떤 사고를 일으킬 수 있는지 논하라. 코드 8-3이 ①을 고른 이유는 무엇이라고 생각하는가?
스스로 점검 (해답은 권말)
- 내삽과 외삽 중 Antoine 식에서 허용되는 쪽은 어느 것인가?
- 계산 온도가 유효범위 안이라면 결과의 오차는 0인가?
8.4 matplotlib로 증기압 곡선 그리기
수치 하나는 한 점만 검증하지만, 곡선은 경향 전체를 한눈에 검증하게 해 준다. 기울기가 물리적 직관과 맞는 방향인지, 대조점이 곡선 위에 앉는지, 상수 행이 바뀌는 경계에서 곡선이 어긋나지는 않는지가 전부 그래프에서 보인다. 파이썬의 표준 시각화 라이브러리 matplotlib로 시작해 보자.
가장 작은 코드부터 그려 본다.
import numpy as np
import matplotlib.pyplot as plt
A, B, C = 5.08354, 1663.125, -45.622 # 물, 344~373 K 행 (NIST, log10·bar·K)
T = np.linspace(344, 373, 60) # K — 유효범위 안에서만 생성
p = 10 ** (A - B / (T + C)) # bar
plt.plot(T, p)
plt.show()
곡선은 나왔다. 그런데 이 그림을 일주일 뒤에 다시 열면 가로축이 K인지 °C인지, 세로축이 bar인지 kPa인지 알 길이 없다. 축 라벨과 단위가 없으면 그 그래프는 검증하기 어렵다. 라벨을 달고, 대조점을 함께 찍고, 여러 상수 행을 겹쳐 넓은 온도 구간을 덮도록 개선을 요구해 보자.
물의 Antoine 상수 행 4개(273~303, 304~333, 334~363, 344~373 K)를 각각 자기 범위 안에서만 그려서 한 그래프에 겹쳐 줘. 세로축은 로그 스케일, 축 라벨에 단위 포함, NIST 정상 끓는점(373.17 K, 1.013 bar)을 대조점으로 표시하고 png로 저장해 줘.
rows = [ # 물 상수 (log10·bar·K, NIST WebBook)
(273, 303, 5.40221, 1838.675, -31.737),
(304, 333, 5.20389, 1733.926, -39.485),
(334, 363, 5.0768, 1659.793, -45.854),
(344, 373, 5.08354, 1663.125, -45.622),
]
for t_min, t_max, A, B, C in rows:
T = np.linspace(t_min, t_max, 60) # K
plt.semilogy(T, 10 ** (A - B / (T + C)), label=f"{t_min}~{t_max} K")
plt.scatter([373.17], [1.013], marker="x", color="k", # NIST 끓는점 = 대조점
zorder=3, label="NIST 정상 끓는점")
plt.xlabel("T (K)")
plt.ylabel("p* (bar)")
plt.legend(); plt.grid(True)
plt.savefig("water_vapor_pressure.png", dpi=150)
✔ 각 행이 자기 유효범위 안에서만 그려지는지 np.linspace의 경계로 확인했다(외삽 없음). 실행하면 네 조각 곡선이 매끄럽게 이어지고, 검은 ×(끓는점 대조점)가 마지막 조각의 끝에 정확히 얹힌다. 겹치는 구간(344~363 K)에서 두 행의 곡선이 거의 포개진다. 행 간 일관성까지 눈으로 검증된 셈이다.
세로축을 로그로 그린 데는 이유가 있다. 식 (8.2)에서 ln p*가 1/T에 대략 직선이었으므로, 로그 축에서는 곡선이 완만해져 전 구간의 경향과 이탈점이 함께 보인다. 100 K 구간에서 증기압이 30배 넘게 변하므로 선형 축으로는 저온부가 바닥에 붙어 아무것도 읽을 수 없다.
AI 협업 사이클 ① 문제 정의 → ② 분해 → ③ 프롬프트 → ④ 생성 코드 읽기 → ⑤ 테스트 → ⑥ 화공 검증
스스로 점검 (해답은 권말)
- 코드 8-5에서 세로축을 로그 스케일로 그린 이유는 무엇인가?
- 코드 8-6은 끓는점 검증을 통과했다. 그런데도 틀린 코드인 이유는 무엇인가?
실습 8: 도구⑤ Antoine 증기압 계산기 (45분)
이번 실습부터 Phase 3의 도구 사다리가 시작된다. 타이머는 45분, 물질은 학번별 배정이다. 빈 손으로 시작해 작동하는 도구와 검증 기록을 남기고 커밋으로 마감한다.
준비물
- 5주차 도구④(단위환산 + 물성 조회기) repo. NIST 조회 흐름을 재사용한다.
- 파이썬 환경에 numpy, matplotlib, pytest 설치 확인 (
python -c "import numpy, matplotlib, pytest"가 조용히 끝나면 정상). - 강의 repo의 학번별 물질 배정표. 본인 학번의 물질을 확인하라.
과제
- 실습 8.1 배정된 물질의 Antoine 상수를 NIST WebBook에서 직접 찾아
constants.py에 기록하라(로그 밑·압력 단위·온도 단위·유효범위·출처 주석 포함). (완료 확인: 4종+출처가 주석으로 남은 파일이 커밋된다) - 실습 8.2 유효범위 검사가 내장된 증기압 함수를 바이브코딩으로 만들라(코드 8-3과 같은 동작으로, 범위 밖이면
ValueError). (완료 확인: 범위 밖 온도에서 에러 메시지에 입력 온도와 범위가 찍힌다) - 실습 8.3 pytest 테스트 3개를 AI에게 생성시키고 직접 읽어 승인하라(① 범위 안 정상 계산 ② 대조값(끓는점) 허용오차 내 일치 ③ 범위 밖
ValueError발생). (완료 확인:pytest test_antoine.py3개 통과 후 커밋) - 실습 8.4 본인 물질의 증기압 곡선을 그려 png로 저장하라(축 라벨+단위, 유효범위 안에서만, NIST 대조점 1개 표시). (완료 확인: png 파일이 repo에 커밋된다)
- 실습 8.5 README에 검증 루틴 3종(단위·대조값·극한값)의 수행 결과를 기록하고 최종 커밋하라. (완료 확인: README에 세 항목이 값과 함께 남는다)
완성 기준
pytest test_antoine.py3개 통과- README에 검증 3종 기록 (대조값의 출처 명기 포함)
- 커밋 ≥ 3 (단계마다 1커밋이 자연스럽다)
- 45분 내 push. 미완이어도 push하라(통과/재도전 판정 기준은 부록 E 참조).
막혔는가?
- 에러가 났다면, 메시지 전문을 그대로 붙여넣고 물어라. "이 에러의 원인 후보 3개와 각각의 확인 방법을 알려 줘."
- 테스트가 안 짜진다면 이렇게 물어라. "pytest로 ValueError 발생을 검사하는 테스트를 짜 줘. pytest.raises를 쓰고, 왜 그렇게 쓰는지 주석으로 설명해 줘."
- 그래프가 이상하다면 이렇게 물어라. "이 코드가 그리는 곡선의 세로축 범위를 예측해 줘. 내 실행 결과와 다른데, 단위가 어디서 바뀌는지 짚어 줘."
퇴실 전 기록 오늘 만든 것·막힌 것·틀린 것 중 1건을 위키에 커밋해야 퇴실이다. 오늘의 후보: 본인 물질의 상수 4종 표, 외삽 에러를 처음 만난 순간의 스크린샷.
요약
- S 8-1 Antoine 식: log₁₀ p* = A − B/(T + C). 경험식이므로 상수의 로그 밑·압력 단위·온도 단위·유효 온도범위 4종을 확인한 뒤에만 쓴다.
- S 8-2 외삽 금지의 코드화: 범위 밖 입력은 값을 돌려주지 않고
ValueError를 던진다 (코드 8-3 패턴). - S 8-3 대조값은 두 점 이상, 앵커에서 먼 점을 포함하라. 앵커 한 점에서만 맞는 오류(코드 8-6)가 존재한다.
- S 8-4 상수 요구 프롬프트 패턴: "로그 밑·단위·유효범위·출처를 함께. 확인 불가면 모른다고 답하라."
| 실수 유형 | 373 K 근방 결과 | 어느 검증에 걸리는가 |
|---|---|---|
| 없음 (범위 내 행 + log₁₀) | 1.008 bar | 걸리지 않음 (0.5% 이내 일치) |
| 범위 밖 행 사용 (외삽) | 0.759 bar | 대조값(끓는점 한 점에서 즉시 발각) |
ln 오독 (math.exp) | 1.003 bar(통과처럼 보임) | 대조값 2점째 (25 °C에서 7배) + 극한값 sanity |
| °C를 K 자리에 입력 | ~10⁻²⁶ bar | 단위 체크 + 극한값 sanity(곡선이 0에 붙는다) |
시험1로 멈췄던 도구 사다리가 다시 움직였다. 도구④의 물성 조회 위에 오늘 증기압 계산기가 올라섰고, AI 협업 사이클에서는 ⑥ 화공 검증이 구체적인 기술(상수 4종 확인, 두 점 대조, 외삽 거부)로 바뀌었다. 다음 장에서 이 함수는 혼자가 아니라 둘씩 쓰인다. 두 물질의 증기압이 만나는 곳에서 증류가 시작된다.
용어 정리
- 증기압(vapor pressure)
- 기체가 자신의 액체 또는 고체와 동적 평형으로 공존할 때의 압력. 물질과 온도의 함수.
- 동적 평형(dynamic equilibrium)
- 분자의 탈출과 복귀가 같은 속도로 계속되어 거시적으로는 변화가 없어 보이는 상태.
- 끓는점(boiling point)
- 증기압이 외부 압력과 같아져 액체 내부에서 기포가 자랄 수 있는 온도.
- 정상 끓는점(normal boiling point)
- 외부 압력 1 atm(= 101.325 kPa)에서의 끓는점.
- 경험식(empirical correlation)
- 이론 유도가 아니라 측정 데이터에 맞춰 만든 식. 적용 범위를 데이터가 결정한다.
- Antoine 식(Antoine equation)
- log₁₀ p* = A − B/(T + C) 꼴의 증기압 경험식.
- 유효 온도범위(valid temperature range)
- 상수를 맞출 때 사용한 데이터가 덮는 온도 구간. 이 밖에서는 식의 정확도가 보증되지 않는다.
- 내삽(interpolation)
- 데이터(유효범위) 안쪽에서 상관식을 사용하는 것.
- 외삽(extrapolation)
- 데이터(유효범위) 밖에서 상관식을 사용하는 것. 이 장의 금지 대상.
연습문제
Q군: 개념·읽기 (AI 없이 풀 것)
- Q8.1 코드 8-1의 목적과 동작을 한국어 2~3문장으로 설명하라. 인자 4개가 각각 무엇이고 어떤 단위 약속을 지는지 포함하라.
- Q8.2 표 8-1의 273~303 K 행으로 T = 298.15 K를 계산하면 결과의 자릿수는 어느 쪽에 가까운가? 0.03 bar인가, 0.3 bar인가? 계산기 없이 지수부의 부호와 크기만으로 추정하고 근거를 설명하라.
- Q8.3 코드 8-3의 계산기를 일부러 틀리게 만드는 방법을 두 가지 제시하고, 각각이 검증 루틴 3종(단위·대조값·극한값) 중 무엇에 가장 먼저 걸리는지 설명하라. 두 방법 중 하나는 "끓는점 한 점 대조만으로는 잡히지 않는 오류"여야 한다.
P군: 제작·계산 (AI 사용 전제)
- P8.1 학번별 배정 물질의 Antoine 상수를 NIST WebBook에서 확보해 세 온도(유효범위의 하단·중앙·상단)에서 증기압을 계산하고, 검증 루틴 3종의 수행 결과를 README 형식으로 작성하라. 대조값의 출처를 명기하라.
- P8.2 아래 벤젠 상수로 정상 끓는점(p* = 1.013 bar가 되는 온도)을 역산하는 코드를 만들라. 계산 결과는 약 349 K가 나온다. 문헌의 벤젠 정상 끓는점은 약 80 °C(약 353 K)다. 4 K 차이의 원인 후보를 두 가지 제시하고, NIST WebBook에서 이 상수의 유효범위를 직접 확인하는 절차를 서술하라.
벤젠 Antoine 상수: log₁₀ p* = A − B/(T + C), p* [bar], T [K] (출처: LibreTexts Foundations 3.09, 유효범위 미명시. 이 표가 4종 확인 규칙의 무엇을 위반하는지부터 지적하라.) A B C 4.60362 1701.073 20.806 - P8.3 표 8-1의 다섯 행을 모두 내장하고, 입력 온도에 맞는 행을 자동 선택하는 증기압 함수를 만들라. 요구 사항: ① 어느 행도 덮지 않는 온도(예: 375 K)에서는 에러와 함께 가장 가까운 행을 안내할 것 ② 두 행이 겹치는 온도(예: 350 K)의 처리 방침을 스스로 정하고 docstring에 근거와 함께 기록할 것 ③ 273~573 K 전체 곡선을 semilogy로 그려 행 경계가 이어지는지 눈으로 검증할 것.
위키 기록 과제 (이번 주 의미 커밋 ≥ 1이 채점 지표다)
- 이 장의 핵심 1페이지 정리를
#seed태그로 커밋하라(Antoine 식, 4종 확인 규칙, 외삽 금지를 본인의 말로). - "내가 틀렸던 것" 1건을 기록하라(이번 실습에서 만난 에러 메시지나 어긋난 계산값의 스크린샷 포함).
- 타 과목 연결 1건. 일반화학의 증기압·끓는점 단원, 또는 화공양론의 포화·습도 계산과 오늘 도구가 만나는 지점을 한 문단으로 적어라.
9장 Raoult 법칙과 기액평형
이번 주의 도구: 이성분계 T-xy 다이어그램
다음 상황을 생각해 보자. 실험대 위에 벤젠과 톨루엔을 반씩 섞은 플라스크가 있다. 순수한 벤젠은 1 atm에서 약 349 K(약 76 °C)에 끓고, 순수한 톨루엔은 약 383 K(약 110 °C)에 끓는다. 그러면 이 혼합물은 몇 도에서 끓기 시작하는가. 두 값의 평균일 것 같지만, 계산해 보면 평균보다 낮은 363 K에서 첫 기포가 올라온다.
기포 속 증기를 분석하면 벤젠이 73%다. 액체는 반반인데 증기는 벤젠 쪽으로 쏠려 있다. 끓이고 응축하기를 반복하면 벤젠만 골라낼 수 있다는 뜻이고, 정유공장 증류탑이 하는 일도 이 반복에 해당한다. 이번 주에는 이 "몇 도에서 끓고, 증기 조성이 무엇인가"를 온도 전 구간에서 한 번에 보여 주는 그림, T-xy 다이어그램을 그리는 도구를 만든다.
이 장에서 다루는 것:
- 이상용액과 라울의 법칙을 도입하고 분압·전압 계산에 적용한다
- 기포점과 이슬점을 정의하고 반복 계산 없이 직접 구하는 알고리즘을 세운다
- 온도 스윕으로 이성분계 T-xy 다이어그램을 그리는 코드를 AI와 함께 만든다
- 기포점 공식과 이슬점 공식을 뒤바꾸는 AI 오류를 검증 루틴으로 잡아낸다
이 장을 마치면 다음을 할 수 있다.
- 라울의 법칙으로 이상용액 위 증기의 분압과 전압을 계산할 수 있다.
- 기포점·이슬점을 정의하고, 온도가 주어졌을 때 평형 조성을 반복 없이 직접 계산할 수 있다.
- "x에서 T를 찾는" 문제를 "T에서 x를 찾는" 문제로 뒤집어 알고리즘을 단순화할 수 있다.
- 온도 스윕 방식으로 T-xy 다이어그램을 그리는 파이썬 도구를 AI와 함께 만들 수 있다.
- T-xy 다이어그램에서 임의의 (조성, 온도) 점이 어느 상 영역에 있는지 읽을 수 있다.
- AI가 생성한 기액평형 코드의 공식 혼동을 극한값 검증으로 잡아낼 수 있다.
이번 주 실습에서는 9.2~9.3절의 알고리즘으로 도구⑥ 이성분계 T-xy 다이어그램을 만든다(타이머 60분). 8장에서 만든 도구⑤의 증기압 함수를 그대로 import하므로, 도구 사다리가 처음으로 "이전 도구 재사용"을 요구하는 주다. 최종 시험 주제 예시 ①이 "학번별 이성분계의 기포점/이슬점 계산기 + T-xy 작도"이므로 이번 실습은 최종 실기의 축소판이기도 하다. 실습 말미에 서브프로젝트 주제 풀(pool)이 공개된다.
9.1 혼합물의 기액평형과 이상용액
8장에서 다룬 것은 순수한 물질 하나였다. 물질이 하나면 이야기가 단순하다. 주어진 압력에서 끓는 온도는 하나로 정해지고, 그 온도는 Antoine 식으로 계산한 증기압이 외부 압력과 같아지는 지점이다. 액체와 증기가 함께 존재하며 더 이상 겉보기 변화가 없는 상태를 기액평형(vapor-liquid equilibrium, VLE)이라 한다.
혼합물이 되는 순간 변수가 하나 늘어난다. 온도와 압력만이 아니라 조성이 상태를 결정하기 때문이다. 액상에서 성분 A의 몰분율을 xA, 기상에서의 몰분율을 yA로 구분해 쓴다. 장 도입의 플라스크에서 액체는 x벤젠 = 0.5인데 첫 기포는 y벤젠 = 0.73이었다. 두 값이 다르다는 사실이 이 장 전체의 출발점이다.
9.1.1 분압과 라울의 법칙
기상이 이상기체처럼 거동하면, 성분 i가 전체 압력 P에 기여하는 몫을 분압(partial pressure) pi라 하고 다음이 성립한다.
pi = yi P (9.1)
이제 액상 쪽을 보자. 액체 혼합물 위의 증기에서 각 성분이 얼마나 증발해 나오는가. 분자 수준에서 서로 밀고 당기는 힘이 비슷한 분자들의 혼합물을 이상용액(ideal solution)이라 한다. 구조나 작용기가 닮은 물질끼리 섞였을 때가 대표적이고, 둘 다 무극성이며 크기가 비슷한 벤젠-톨루엔이 교과서적인 예다. 벤젠 분자 입장에서는 이웃이 벤젠이든 톨루엔이든 받는 힘이 비슷하므로, 증발해 나가려는 경향은 순수 벤젠일 때와 같고 다만 표면을 차지한 비율만큼 줄어든다.
이 직관을 식으로 쓴 것이 라울의 법칙(Raoult's law)이다. 이상용액에서 성분 i의 분압은 그 성분의 액상 몰분율에 순수 증기압을 곱한 값이 된다.
pi = xi p*i(T) (9.2)
여기서 p*i(T)는 온도 T에서 순수한 성분 i의 증기압이고, 8장의 Antoine 식으로 계산하는 바로 그 양이다. 식 (9.1)과 식 (9.2)에서 분압을 소거하면 기상 조성과 액상 조성을 잇는 평형 관계를 얻는다.
yi P = xi p*i(T) (9.3)
전체 압력은 분압의 합이므로, 이성분계(성분 A, B)에서 식 (9.2)를 더하면 전압이 나온다.
P = xA p*A(T) + (1 − xA) p*B(T) (9.4)
라울의 법칙은 언제 믿을 수 있는가. 두 성분의 분자 간 상호작용이 충분히 닮았을 때, 그리고 각 성분이 무시할 수 없는 양으로 존재할 때다. 에탄올-물처럼 수소결합이 강하게 개입하는 계는 이 가정에서 멀어진다. 이 장의 도구는 이상용액 가정 위에 서 있으므로, 어떤 물질 쌍에 적용할지 자체가 공학적 판단임을 기억해 두자.
9.1.2 Antoine 상수 준비: 8장 도구의 재사용
식 (9.3)의 p*i(T)를 계산하려면 두 성분의 Antoine 상수가 필요하다. 이 장의 예제는 전부 벤젠-톨루엔 계를 쓰며, 상수는 표 9-1과 같다.
표 9-1 벤젠·톨루엔의 Antoine 상수: log10(p*/bar) = A − B/(T/K + C) 형식
| 물질 | A | B | C | 로그 밑 / 압력 / 온도 | 유효 온도범위 |
|---|---|---|---|---|---|
| 벤젠 | 4.60362 | 1701.073 | 20.806 | log10 / bar / K | NIST WebBook에서 직접 확인하라 |
| 톨루엔 | 4.07827 | 1343.943 | −53.773 | log10 / bar / K | NIST WebBook에서 직접 확인하라 |
유효범위 칸을 비워 두지 않고 "직접 확인하라"고 적은 데는 이유가 있다. 이 상수의 수록 출처(LibreTexts, NIST 인용)가 유효 온도범위를 함께 싣지 않았기 때문이다. 8장에서 유효범위 밖 외삽이 25% 오차를 만드는 것을 보았다. 범위가 명시되지 않은 상수는 범위를 찾아내는 것까지가 사용자의 일이며, 이번 실습의 첫 단계가 바로 그 확인 작업이다.
예제 9.1-1 등몰 벤젠-톨루엔 용액 위의 증기
문제 368 K에서 벤젠 몰분율 0.5인 벤젠-톨루엔 액체 혼합물이 증기와 평형에 있다. 이 온도에서 벤젠과 톨루엔의 순수 증기압은 각각 1.692 bar, 0.633 bar이다. 전압 P와 기상 조성 y벤젠을 구하라.
풀이 전략 미지수는 P와 y벤젠 둘, 식은 (9.4)와 (9.3) 둘이다. 손계산으로 충분한 크기이므로 AI 없이 직접 푼다. 전압을 먼저 구해야 식 (9.3)에서 y를 나눌 분모가 생긴다.
계산 식 (9.4)에 값을 대입하면 다음을 얻는다.
P = 0.5 × 1.692 + 0.5 × 0.633 = 1.163 bar
식 (9.3)을 y에 대해 정리해 대입하면 기상 조성이 나온다.
y벤젠 = x벤젠 p*벤젠 / P = 0.5 × 1.692 / 1.163 = 0.728
- 단위 체크: bar × 무차원 / bar이므로 y는 무차원 몰분율. 통과.
- 대조값: y벤젠 + y톨루엔 = 0.728 + 0.272 = 1.000. 몰분율 합이 1이다. 통과.
- 극한값: x벤젠 → 1이면 식 (9.4)에서 P → p*벤젠, 즉 순수 벤젠으로 되돌아간다. 물리적으로 타당.
분석 답: P = 1.163 bar, y벤젠 = 0.728. 액체는 반반인데 증기는 벤젠이 73%다. 증기압이 큰 성분(끓는점이 낮은 성분)을 휘발성이 크다고 하며, 휘발성이 큰 성분은 언제나 액상보다 기상에 풍부하다. 이 비대칭이 증류 분리의 물리적 원리다. x벤젠을 0.2, 0.8로 바꿔 같은 계산을 반복해 보라. y > x 관계가 유지되는지 확인하는 것이 What-if 과제다.
스스로 점검 (해답은 권말)
- 식 (9.3)에서 xi와 yi 중 액상 조성은 어느 쪽인가?
- 368 K에서 x벤젠 = 1일 때 식 (9.4)가 주는 전압은 얼마인가?
- 벤젠-톨루엔에 라울의 법칙을 적용할 수 있는 근거를 한 문장으로 말하라.
9.2 기포점과 이슬점: 반복을 없애는 발상
액체 혼합물을 일정한 압력에서 가열할 때 첫 기포가 생기는 조건을 기포점(bubble point)이라 한다. 반대로 증기 혼합물을 냉각할 때 첫 액체 방울이 맺히는 조건을 이슬점(dew point)이라 한다. 순수 물질이라면 둘은 같은 온도(끓는점)지만, 혼합물에서는 기포점과 이슬점이 서로 다른 온도가 된다. 장 도입의 반반 혼합물은 363 K에서 끓기 시작해 371 K이 되어서야 마지막 액체 방울이 사라진다.
9.2.1 온도가 주어지면 직접 계산이다
온도를 알고 압력을 구하는 문제는 반복이 필요 없다. 기포점에서는 액상 조성이 전체 조성과 같으므로(증기가 아직 거의 없다), 식 (9.4)에 대입만 하면 기포 압력이 나온다. 이슬점 쪽도 마찬가지다. 식 (9.3)을 xi = yiP/p*i로 정리하고 액상 몰분율의 합이 1이라는 조건을 쓰면 다음을 얻는다.
1/P = yA/p*A + yB/p*B (9.5)
식 (9.4)는 기포 압력, 식 (9.5)는 이슬 압력을 대입 한 번으로 준다. 두 식의 생김새를 눈에 담아 두자. 기포 쪽은 증기압의 가중 산술평균, 이슬 쪽은 역수의 가중합이다. 뒤에서 AI가 이 둘을 뒤바꾸는 사고를 다룬다.
9.2.2 온도를 구하는 문제는 왜 성가신가
실무에서 더 흔한 질문은 반대 방향이다. "압력이 1 atm으로 고정돼 있을 때 이 혼합물은 몇 도에서 끓는가?" 이번에는 T가 미지수인데, T는 Antoine 식의 지수 안에 두 번 들어가 있어 식 (9.4)를 T에 대해 대수적으로 풀 수 없다. 고전적인 처방은 시행착오다. 온도를 추측하고, 그 온도에서 yi를 계산해 합을 확인하고, 합이 1이 될 때까지 추측을 고친다.
예제 9.2-1 등몰 벤젠-톨루엔의 기포점 온도: 시행착오법
문제 P = 1 atm(= 1.013 bar)에서 x벤젠 = 0.5인 벤젠-톨루엔 혼합물의 기포점 온도와 첫 증기의 조성을 구하라. Antoine 상수는 표 9-1을 쓴다.
풀이 전략 판정 기준은 Σyi = 1이다. 추측 온도에서 Σyi > 1이면 증기압이 과했다는 뜻이므로 온도를 내리고, Σyi < 1이면 온도를 올린다. 초기 추측은 두 순수 끓는점(349 K, 384 K)의 중간인 368 K로 잡는다(초기값은 반복 횟수만 바꿀 뿐 답을 바꾸지 않는다).
계산 1차 추측 T = 368 K. 예제 9.1-1에서 이미 구한 증기압을 재사용하면 식 (9.3)에서 다음을 얻는다.
y벤젠 = 0.5 × 1.692/1.013 = 0.835, y톨루엔 = 0.5 × 0.633/1.013 = 0.312
합은 0.835 + 0.312 = 1.147 > 1. 추측이 높았으므로 온도를 내려 다시 계산한다. 몇 차례 반복하면 T = 363 K에서 p*벤젠 = 1.484 bar, p*톨루엔 = 0.540 bar가 되고,
y벤젠 = 0.5 × 1.484/1.013 = 0.732, y톨루엔 = 0.5 × 0.540/1.013 = 0.267
합이 0.999 ≈ 1이 되어 반복이 끝난다.
- 단위 체크: 추측 온도를 K로 넣었는가. 표 9-1의 상수는 K 전용이다. 통과.
- 대조값: 수록 출처(LibreTexts)가 엑셀 solver로 구한 기포점 363.04 K와 일치. 통과.
- 극한값: 답은 두 순수 끓는점 사이(349 K < 363 K < 384 K)에 있어야 한다. 통과.
분석 답: 기포점 온도 363 K, 첫 증기 조성 y벤젠 = 0.732. 장 도입의 두 숫자가 여기서 나왔다. 다만 이 풀이는 손으로 서너 번 반복해야 하고, 코드로 짜면 반복문과 수렴 판정이 필요하다. 반복 자체가 잘못은 아니지만, 우리가 정말 원하는 것이 "한 조성의 기포점"이 아니라 "모든 조성의 기포점 곡선"이라면 더 좋은 길이 있다. 다음 소절에서 문제를 뒤집는다. What-if: x벤젠 = 0.2로 바꿔 기포점이 383 K 쪽으로 이동하는지 확인해 보라.
9.2.3 문제를 뒤집으면 반복이 사라진다
시행착오가 필요했던 이유는 T를 미지수로 두었기 때문이다. 그렇다면 T를 미지수가 아니라 입력으로 만들면 된다. "조성 x가 주어졌을 때 끓는 온도 T를 찾아라" 대신 "온도 T가 주어졌을 때 그 온도에서 끓는 조성 x를 찾아라"라고 묻는 것이다. 식 (9.4)를 xA에 대해 풀면 나눗셈 한 번짜리 공식이 나온다.
xA(T) = (P − p*B(T)) / (p*A(T) − p*B(T)) (9.6)
그 온도의 기상 조성도 식 (9.3)에서 바로 따라온다.
yA(T) = xA(T) p*A(T) / P (9.7)
정말 맞는지 확인해 보자. T = 363 K를 식 (9.6)에 넣으면 x벤젠 = (1.013 − 0.540)/(1.484 − 0.540) = 0.501이다. 예제 9.2-1에서 시행착오로 찾은 "x = 0.5의 기포점이 363 K"와 정확히 같은 사실을 이번에는 반복 없이 얻었다. 같은 문제의 두 표현인데 한쪽은 반복이 필요하고 다른 쪽은 대입 한 번으로 끝난다.
두 순수 끓는점 사이에서 T를 촘촘히 훑으며(이를 온도 스윕이라 부르자) 식 (9.6)·(9.7)을 계산하면, (x, T) 점들의 모음과 (y, T) 점들의 모음이 생긴다. 이 두 곡선이 바로 다음 절의 T-xy 다이어그램이다. 특정 조성의 기포점이 필요하면 곡선에서 보간으로 읽으면 되므로, 반복 계산은 끝까지 등장하지 않는다.
스스로 점검 (해답은 권말)
- 기포점 온도 추측에서 Σyi = 0.91이 나왔다. 다음 추측 온도는 올려야 하는가, 내려야 하는가?
- 식 (9.6)에서 T가 순수 B의 끓는점일 때 xA는 얼마가 되는가?
- 식 (9.5)로 이슬 압력을 구할 때 반복 계산이 필요 없는 이유를 한 문장으로 말하라.
생각해보기 식 (9.6)은 압력 P가 두 순수 증기압 사이에 있을 때만 0과 1 사이의 x를 준다. T = 390 K(두 물질 모두의 끓는점보다 높음)를 넣으면 x가 어떤 값이 되고, 그것은 물리적으로 무엇을 뜻하는가? 코드에서 이런 입력을 어떻게 처리해야 할지 두 가지 방법을 제안하라.
9.3 T-xy 다이어그램: 읽는 법과 그리는 법
고정된 압력에서 온도를 세로축에, 조성을 가로축에 놓고 기포점 곡선과 이슬점 곡선을 함께 그린 그림을 T-xy 다이어그램이라 한다. 가로축 하나에 액상 몰분율 x와 기상 몰분율 y를 같이 표시하기 때문에 붙은 이름이다. 이 그림 한 장에는 그 압력에서 그 혼합물이 보일 수 있는 모든 끓음·응축 거동이 들어 있다.
9.3.1 세 영역과 두 곡선
두 곡선은 그림을 세 영역으로 가른다. 아래쪽은 전부 액체인 영역, 위쪽은 전부 증기인 영역, 두 곡선 사이가 액체와 증기가 공존하는 기액평형 영역이다. 액체가 낮은 에너지 상태이므로 저온 쪽이 액체 영역인 것은 당연하다. 기포점들을 이은 아래 곡선을 포화 액체선(saturated liquid line), 이슬점들을 이은 위 곡선을 포화 증기선(saturated vapor line)이라 하며, 포화 증기선은 언제나 포화 액체선 위에 있다. 온도가 높을수록 증기 쪽이 유리하기 때문이다.
그림의 양 끝은 순수 물질이다. x = 1(순수 벤젠)에서 두 곡선은 벤젠의 끓는점 약 349 K에서 만나고, x = 0(순수 톨루엔)에서는 약 383 K에서 만난다. 순수 물질은 기포점과 이슬점이 같으므로 두 곡선이 한 점으로 모이는 것이다. 이 성질은 잠시 뒤 코드 검증의 극한값 체크로 그대로 쓰인다.
기액평형 영역 안의 한 점, 예컨대 전체 조성 0.5인 혼합물을 두 곡선 사이의 온도까지 가열한 상태를 보자. 이 점에서 수평선을 그으면(같은 온도에서 액체와 증기가 공존하므로) 포화 액체선과 만나는 점의 가로좌표가 액상 조성 x, 포화 증기선과 만나는 점의 가로좌표가 기상 조성 y다. 이 수평선을 tie line이라 부른다.
9.3.2 두 상의 양은 얼마인가: 지렛대 규칙
tie line은 조성만이 아니라 양도 알려 준다. 전체 조성을 z라 하면 벤젠에 대한 물질수지 x·n액 + y·n기 = z·n전체에서 다음을 얻는다.
n액/n전체 = (z − y) / (x − y) (9.8)
전체 조성 점에서 각 곡선까지의 거리 비로 두 상의 양이 갈린다고 해서 지렛대 규칙(lever rule)이라 한다. 예를 들어 z = 0.5인 혼합물이 어느 온도에서 x ≈ 0.39, y ≈ 0.62로 갈라졌다면 액체 분율은 (0.5 − 0.62)/(0.39 − 0.62) ≈ 0.52, 대략 절반씩이다. 전체 조성 점이 어느 곡선에 가까울수록 그 상이 많다. 기포점 직후에는 점이 포화 액체선에 붙어 있으니 계는 거의 전부 액체다.
스스로 점검 (해답은 권말)
- T-xy 다이어그램에서 (조성 0.5, 350 K) 점은 어느 영역인가? (벤젠-톨루엔, 1 atm)
- 식 (9.8)에서 z = x이면 액체 분율은 얼마인가? 물리적으로 어떤 순간인가?
- 포화 증기선이 포화 액체선보다 항상 위에 있는 이유를 한 문장으로 말하라.
9.3.3 그리는 법: 온도 스윕 알고리즘
9.2.3절의 발상을 그대로 절차로 옮기면 T-xy 다이어그램 작도 알고리즘이 된다. 반복문은 있지만 수렴 판정은 없다. 모든 단계가 대입 계산이다.
- 두 순수 성분의 끓는점을 구해 스윕 구간 [T낮은 끓는점, T높은 끓는점]을 정한다.
- 구간을 잘게 나눈 온도 배열을 만든다.
- 각 온도에서 두 성분의 증기압을 Antoine 식으로 계산한다.
- 식 (9.6)으로 x, 식 (9.7)로 y를 계산한다.
- (x, T)를 포화 액체선으로, (y, T)를 포화 증기선으로 그린다.
이 절차를 AI에게 시켜 도구⑥의 뼈대를 만들어 보자.
예제 9.3-1 벤젠-톨루엔 T-xy 다이어그램 작도
문제 표 9-1의 Antoine 상수를 써서 1 atm(1.013 bar)에서 벤젠-톨루엔 T-xy 다이어그램을 그리는 파이썬 스크립트를 만들라. 8장 도구⑤의 증기압 함수를 재사용한다.
풀이 전략 계산 로직은 위 5단계 알고리즘으로 이미 분해해 두었으므로, 프롬프트에는 사용할 식·단위·재사용할 함수 이름까지 지정한다. AI에게 맡기는 것은 numpy·matplotlib 문법이고, 알고리즘 설계와 검증은 우리 몫이다.
8장에서 만든 vapor_pressure.py의 antoine_p_bar(T, A, B, C) 함수를 import해서 벤젠-톨루엔 T-xy 다이어그램을 그리는 스크립트를 만들어 줘. 압력은 1.013 bar 고정. 온도를 349.2 K부터 383.8 K까지 200등분해서 각 온도에서 x = (P - p_톨루엔)/(p_벤젠 - p_톨루엔), y = x * p_벤젠 / P를 계산하고, (x,T)와 (y,T)를 한 그래프에 그려 줘. 주석은 한국어로, 물리량 변수에는 단위 주석을 달아 줘.
import numpy as np
import matplotlib.pyplot as plt
from vapor_pressure import antoine_p_bar # 도구⑤(8장) 재사용
# Antoine 상수 (log10 / bar / K) — 출처: NIST WebBook
BENZENE = (4.60362, 1701.073, 20.806)
TOLUENE = (4.07827, 1343.943, -53.773)
P = 1.013 # bar (= 1 atm)
T = np.linspace(349.2, 383.8, 200) # K, 두 순수 끓는점 사이
p_b = antoine_p_bar(T, *BENZENE) # bar, 벤젠 증기압
p_t = antoine_p_bar(T, *TOLUENE) # bar, 톨루엔 증기압
x = (P - p_t) / (p_b - p_t) # 식 (9.6), 무차원
y = x * p_b / P # 식 (9.7), 무차원
plt.plot(x, T, label="포화 액체선 (기포점)")
plt.plot(y, T, label="포화 증기선 (이슬점)")
plt.xlabel("벤젠 몰분율 x, y")
plt.ylabel("온도 T [K]")
plt.title("벤젠-톨루엔 T-xy (P = 1.013 bar)")
plt.legend()
plt.grid(True)
plt.savefig("txy_benzene_toluene.png", dpi=150)
검증: 그래프 양 끝에서 두 곡선이 349.2 K와 383.8 K로 모이는지 확인했고, 순수 끓는점과 일치한다. x = 0.5에서 포화 액체선을 읽으면 약 363 K로 예제 9.2-1의 시행착오 결과와 일치한다. y = 0.5에서 포화 증기선을 읽으면 약 371 K로 수록 출처의 이슬점 371.2 K와 일치한다. 세 점 모두 통과 후 코드를 채택했다.
코드 9-2가 저장하는 그림(그림 9-1)에는 왼쪽 위로 완만하게 휘어진 두 곡선이 나타난다. 아래가 포화 액체선, 위가 포화 증기선이고, 두 곡선은 양 끝에서 순수 끓는점으로 모인다.
- 단위 체크: T는 K, 증기압과 P는 모두 bar이고, 식 (9.6)의 분자·분모가 같은 단위라 x는 무차원. 통과.
- 대조값: x = 0.5의 기포점 363 K가 수록 출처(LibreTexts)의 solver 결과 363.04 K와 일치. 통과.
- 극한값: x → 1에서 T → 349.2 K(순수 벤젠), x → 0에서 T → 383.8 K(순수 톨루엔). 통과.
분석 반복 계산 한 번 없이 다이어그램 전체가 나왔다. 알고리즘을 먼저 5단계로 분해하고 식 번호까지 프롬프트에 넣었기 때문에 생성 코드가 한 번에 의도와 맞았다는 점도 눈여겨보라. 프롬프트의 구체성은 분해의 산물이다. What-if: np.linspace의 점 개수를 200에서 10으로 줄여 재실행하고, 곡선이 어디서부터 각져 보이는지 확인해 보라.
특정 조성의 기포점 온도가 숫자로 필요하면 곡선을 보간해 읽으면 된다. np.interp는 x 배열이 증가 순서일 때 동작하므로, 감소 순서인 배열은 뒤집어 넣는다.
# x = 0.5의 기포점 온도를 보간으로 조회 (x는 T 증가에 따라 감소하므로 뒤집는다)
T_bubble = np.interp(0.5, x[::-1], T[::-1]) # K
print(f"기포점 온도: {T_bubble:.1f} K") # 363.0 K 부근이 나와야 한다
출력이 363.0 K 부근이면 예제 9.2-1과 교차 검증이 끝난 것이다. 시행착오 없이, solver 없이, 배열 두 개와 보간 한 줄로 같은 답에 도달했다.
9.4 AI와 함께 만들 때: 두 공식을 뒤바꾸는 AI
① 문제 정의 → ② 분해 → ③ 프롬프트 → ④ 생성 코드 읽기 → ⑤ 테스트 → ⑥ 화공 검증
이번 장의 무게중심은 ②와 ⑥이다. "T에서 x를 구하는 문제로 뒤집는다"는 결정은 프롬프트를 쓰기 전에 사람이 내리는 분해 판단이고, 기포점·이슬점의 물리적 순서 관계는 사람만 아는 검증 기준이다.
기액평형 계산을 AI에게 시킬 때 좋은 프롬프트와 나쁜 프롬프트의 차이는 9.3.3절에서 이미 드러났다. "T-xy 다이어그램 그려 줘"라고만 하면 AI가 알고리즘 선택(시행착오냐 스윕이냐), 단위, 상수 출처를 전부 스스로 정하고, 그 선택들 하나하나가 검증해야 할 짐이 된다. 식 번호·단위·함수 이름을 지정한 프롬프트는 검증할 것을 "문법이 맞는가" 수준으로 줄여 준다.
그래도 검증을 건너뛸 수는 없다. 기포점과 이슬점처럼 짝을 이루는 개념은 AI가 특히 자주 뒤섞는 지점이기 때문이다.
이 사례가 보여 주는 교훈은 3장의 원칙 그대로다. 실행되는 코드와 옳은 코드는 다르며, 그 간극을 메우는 것은 화공 지식으로 만든 테스트다. 기액평형 도구에서는 "이슬점 ≥ 기포점", "몰분율 합 = 1", "순수 극한에서 끓는점 일치" 세 가지가 언제나 쓸 수 있는 물리 테스트다.
생각해보기 이 장의 도구를 에탄올-물 혼합물에 그대로 적용하면 T-xy 다이어그램이 나오기는 한다. 그 그림을 믿어도 되는가? 9.1.1절의 이상용액 조건에 비추어 판단하고, 계산 결과를 믿기 전에 확인할 방법을 두 가지 제안하라. (힌트: 실측 데이터와의 대조도 검증 루틴의 하나다.)
실습 9: 도구⑥ 이성분계 T-xy 다이어그램 (60분)
준비물
- 8장 도구⑤ 저장소의
vapor_pressure.py. 이번 실습은 이 파일의antoine_p_bar함수를 import한다. 도구⑤가 미완성이면 먼저 완성하라. - 가상환경에 numpy, matplotlib 설치 확인. (확인 커맨드:
python -c "import numpy, matplotlib", 아무 출력이 없으면 정상) - 타이머 60분. 5주차부터의 규칙대로 공개 지목 구술이 있으므로, 어느 줄이든 설명할 수 있는 상태로 커밋하라.
과제 본 실습은 벤젠-톨루엔으로 진행한다. 오늘 만드는 도구가 그대로 최종 실기의 리허설이다(주제 예시는 장 머리의 실습 연결 참조).
- 실습 9.1. NIST WebBook에서 벤젠과 톨루엔의 Antoine 상수와 유효 온도범위를 조회하고, 표 9-1과 대조한 결과를 README에 기록하라. (완료 기준: README에 로그 밑·압력 단위·온도 단위·유효범위 4종이 적혀 있다) 커밋하라.
- 실습 9.2. 도구⑤의 함수를 import해 일정 온도에서 기포 압력(식 9.4)과 이슬 압력(식 9.5)을 반환하는 함수 2개를 만들라. (완료 기준: 368 K, 등몰에서 기포 압력 1.163 bar가 나온다) 커밋하라.
- 실습 9.3. 온도 스윕(식 9.6, 9.7)으로 x, y 배열을 계산하고 T-xy 다이어그램을 png로 저장하라. (완료 기준: 두 곡선이 양 끝에서 순수 끓는점으로 모인다) 커밋하라.
- 실습 9.4.
np.interp로 임의 조성의 기포점·이슬점 온도를 조회하는 함수를 추가하고, x = 0.5에서 363 K, y = 0.5에서 371 K 부근이 나오는지 확인하라. (완료 기준: 두 값이 검증 코멘트와 일치하고, 이슬점 > 기포점이다) - 실습 9.5. 검증 3종(단위·대조값·극한값)의 수행 결과를 README에 기록하고 최종 커밋하라. (완료 기준: README만 읽고도 제3자가 검증을 재현할 수 있다)
완성 기준 (통과 / 재도전 2단계, 판정 기준은 부록 E 참조)
- 테스트 3개 통과: ① 양 끝점 온도가 순수 끓는점과 ±0.5 K 이내 ② x = 0.5의 기포점이 363 ± 1 K ③ 임의 조성에서 이슬점 온도 ≥ 기포점 온도
- README에 검증 루틴 3종 기록 (NIST 대조 스크린샷 또는 수치 인용 포함)
- 의미 있는 커밋 3개 이상 (실습 단계와 대응)
- 60분 내 제출
막혔는가?
- import가 안 될 때는 에러 메시지 전문을 붙여넣고 이렇게 물어라. "이 ImportError의 원인 후보 3개와 각각의 확인 방법을 알려 줘. vapor_pressure.py는 상위 폴더에 있어."
- 그래프가 이상할 때는 이렇게 물어라. "T-xy 다이어그램에서 두 곡선이 교차해 버렸어. 코드를 고치지 말고, 어떤 물리적·논리적 원인이 가능한지 순서대로 점검할 체크리스트를 만들어 줘."
- 보간 결과가 엉뚱할 때는 이렇게 물어라. "np.interp가 이상한 값을 반환해. 입력 배열이 감소 순서일 때 np.interp가 어떻게 동작하는지 설명하고, 내 데이터로 확인하는 한 줄 실험을 제안해 줘."
퇴실 전 위키 기록 오늘 만든 것·막힌 것·틀린 것 중 1건을 위키에 커밋해야 퇴실한다. 추천 소재: 기포점과 이슬점을 헷갈렸던 순간, 또는 문제를 뒤집어 반복을 없앤 발상.
서브프로젝트 주제 풀 공개
오늘 실습 말미에 서브프로젝트 주제 풀이 공개된다. 다음 실습까지 제출할 것은 프로젝트 선언 1쪽이다. 고른 주제, 재사용할 본인 사다리 산출물 2개 이상, 검증 계획을 담는다. 재사용 요건 때문에 오늘까지 만든 도구①~⑥의 완성도가 고를 수 있는 주제의 폭을 정한다. 예컨대 McCabe-Thiele 예비설계 도우미(주제 1)는 도구⑤·⑥을 그대로 확장한다. 일정은 9주 선언 → 13주 스프린트 → 14주 발표(과제 4점 중 발표 2점)다.
요약
- S 9-1 (평형 관계) yi P = xi p*i(T). 라울의 법칙과 분압 법칙의 결합. 이 장 모든 계산의 뿌리.
- S 9-2 (직접식 한 쌍) 기포 압력 P = Σ xi p*i, 이슬 압력 1/P = Σ yi/p*i. 온도가 주어지면 반복 없이 대입 한 번.
- S 9-3 (온도 스윕) x(T) = (P − p*B)/(p*A − p*B), y(T) = x p*A/P. 미지수를 입력으로 뒤집으면 T-xy 전체가 직접 계산이 된다.
- S 9-4 (프롬프트 패턴) 알고리즘을 단계로 분해한 뒤 식·단위·재사용 함수 이름까지 지정해 요청한다. 프롬프트의 구체성은 분해의 산물이다.
표 9-2 기포점과 이슬점 비교
| 기포점 | 이슬점 | |
|---|---|---|
| 정의 | 가열 중 첫 기포가 생기는 조건 | 냉각 중 첫 액체 방울이 맺히는 조건 |
| 알려진 조성 | 액상 x (≈ 전체 조성) | 기상 y (≈ 전체 조성) |
| 직접식 (정온) | P = Σ xi p*i | 1/P = Σ yi/p*i |
| T-xy에서의 위치 | 포화 액체선 (아래) | 포화 증기선 (위) |
| 등몰 벤젠-톨루엔, 1 atm | 363 K | 371.2 K |
| 물리 테스트 | 같은 조성·압력에서 이슬점 온도 ≥ 기포점 온도, 몰분율 합 = 1 | |
도구 사다리는 이제 여섯 칸째다. 이번 주에 남는 것은 T-xy 그림 한 장보다 두 가지 훈련이다. 하나는 AI 협업 사이클의 ② 분해로, 풀기 어려운 미지수를 입력으로 뒤집어 반복 계산을 설계 단계에서 없앴다. 다른 하나는 ⑥ 화공 검증으로, "이슬점 ≥ 기포점" 같은 물리 관계를 테스트로 바꿔 그럴듯한 오답을 잡았다. 도구⑤의 함수를 import하면서 도구가 도구를 이어받는 구조도 처음 체감했을 것이다. 다음 장에서는 이 구조가 물질수지로 확장된다.
용어 정리
- 기액평형 (vapor-liquid equilibrium, VLE)
- 액체와 증기가 함께 존재하며 겉보기 변화가 없는 상태.
- 분압 (partial pressure)
- 기체 혼합물에서 한 성분이 전체 압력에 기여하는 몫. 이상기체에서 pi = yiP.
- 이상용액 (ideal solution)
- 분자 간 상호작용이 서로 비슷한 성분들로 이루어져 라울의 법칙을 따르는 액체 혼합물.
- 라울의 법칙 (Raoult's law)
- 이상용액에서 성분의 분압이 액상 몰분율과 순수 증기압의 곱과 같다는 법칙.
- 기포점 (bubble point)
- 일정 압력에서 액체 혼합물을 가열할 때 첫 기포가 생기는 조건(온도 또는 압력).
- 이슬점 (dew point)
- 일정 압력에서 증기 혼합물을 냉각할 때 첫 액체 방울이 맺히는 조건.
- 포화 액체선 (saturated liquid line)
- T-xy 다이어그램에서 기포점들을 이은 아래쪽 곡선.
- 포화 증기선 (saturated vapor line)
- T-xy 다이어그램에서 이슬점들을 이은 위쪽 곡선.
- 지렛대 규칙 (lever rule)
- 기액공존 영역에서 전체 조성 점과 두 곡선 사이 거리 비로 액체·증기의 양을 구하는 규칙.
연습문제
Q군: 개념·읽기 (AI 없이 풀 것)
- Q9.1 등몰 벤젠-톨루엔 혼합물(1 atm)에 대해 다음 각 온도에서 계가 어느 영역에 있는지 판정하라: 355 K, 367 K, 378 K. 판정 근거를 표 9-2의 두 온도로 설명하라.
- Q9.2 코드 9-1에서
x = (P - p_t) / (p_b - p_t)한 줄의 목적과 동작을, 식 번호를 인용해 한국어 2~3문장으로 설명하라 (EiPE). - Q9.3 어떤 학생의 AI가 이슬 압력을 P = Σ yi p*i로 계산하는 코드를 냈다. 이 코드를 채점하고, 어떤 검증 루틴이 이 오류를 잡아내는지 지목한 뒤 올바른 식으로 교정하라.
P군: 제작·계산 (AI 사용 전제)
- P9.1 표 9-1의 상수로 358 K에서 x벤젠 = 0.3인 혼합물의 기포 압력과 기상 조성을 계산하는 스크립트를 만들라. 검증 3종 결과를 주석으로 남겨라.
- P9.2 도구⑥을 확장해 압력을 인자로 받게 하고, 0.5 bar와 2.0 bar의 T-xy 다이어그램을 한 그림에 겹쳐 그려라. 압력이 오르면 두 곡선이 어느 방향으로 움직이는지 관찰 결과를 README에 쓰고 물리적으로 해석하라.
- P9.3 (도전·메타인지) 도구⑥의 코드에 "실행은 되지만 물리적으로 틀린 답"을 내는 버그를 일부러 두 가지 심어 보라 (예: 단위 혼동, 공식 맞바꿈). 각 버그가 검증 루틴 3종 중 무엇에 걸리는지 표로 정리하고, 어느 버그도 잡지 못하는 검증 항목이 있다면 새 테스트를 제안하라.
이번 주 위키 기록 과제 (주당 의미 커밋 ≥ 1이 채점 지표다)
- 이 장 핵심 1페이지 정리를
#seed태그로 작성하라(표 9-2를 자기 언어로 다시 그리는 것을 추천한다). - "내가 틀렸던 것" 1건을 에러 메시지 또는 잘못된 그래프 스크린샷과 함께 기록하라.
- 타 과목 연결 1건. 물리화학의 상평형, 또는 화공양론의 몰분율·물질수지와 이 장의 식 (9.8)이 어떻게 만나는지 적어라.
10장 물질수지의 알고리즘화
이번 주의 도구: ⑦ 물질수지 solver
다음 상황을 생각해 보자. 화공양론 과제에 분리 공정 문제가 나왔다. 공급 흐름 하나, 생성물 흐름 둘이라 지난주까지는 식 두 개로 끝났다. 그런데 이번 문제의 흐름도에는 화살표가 하나 더 붙어 있다. 하부 흐름의 절반을 되돌려 공급과 섞는 재순환 배관이다.
미지수를 세어 보니 여섯 개다. 식 여섯 개를 세우고 대입을 시작한다. 20분 뒤, 세 번째 이항에서 부호 하나를 잃어버렸다. 처음부터 다시 푼 값은 아까와 다르고, 어느 쪽이 맞는지 확인할 방법이 없다. 문제를 이해하지 못한 것이 아니다. 식은 전부 맞게 세웠다. 무너진 것은 1차식 여섯 줄을 손으로 관리하는 회계 작업이다.
컴퓨터는 정확히 이 작업을 잘한다. numpy는 여섯 개든 예순 개든 연립선형방정식을 함수 호출 한 번으로 푼다. 남는 질문은 하나다. 그 여섯 줄이 올바른 식이라는 것을 누가 보증하는가. 이 장은 그 보증 절차(경계 긋기, 라벨링, 자유도 분석)를 먼저 익히고, 그다음에야 풀이를 numpy에 넘긴다.
이 장에서 다루는 것
- 시스템 경계를 긋고 일반 수지식에서 출발해 정상상태 물질수지를 세운다
- 흐름도 라벨링과 자유도 분석으로 문제의 해결 가능성을 판정한다
- 재순환·퍼지 구조에서 분기점과 합류점의 회계 규칙을 다룬다
- 물질수지를 행렬 형태로 변환해 numpy로 풀고 결과를 검증한다
- 실습에서 학번별 문제로 도구⑦ 물질수지 solver를 완성한다
학습목표 이 장을 마치면 다음을 할 수 있다.
- 공정 서술에서 시스템 경계를 긋고 흐름도를 빠짐없이 라벨링할 수 있다.
- 미지수와 독립 방정식을 세어 자유도를 계산하고 문제가 풀리는지 판정할 수 있다.
- 재순환 시스템에서 분기점·합류점을 공정으로 취급해 자유도 분석을 수행할 수 있다.
- 물질수지 연립방정식을 행렬 형태 Ax = b로 변환하고 numpy로 풀 수 있다.
- Singular matrix 에러를 자유도 관점에서 진단하고 교정할 수 있다.
- solver의 결과를 검증 루틴 3종으로 확인하고 README에 기록할 수 있다.
이번 주 실습에서는 10.2~10.4절을 사용해 도구⑦ 물질수지 solver를 만든다(75분, 학번별 문제, 타이머 가동). 최종 시험의 주제 예시 가운데 "학번별 recycle 공정의 물질수지 정합성 검사 + 흐름 시각화"가 이 장의 범위 그대로다. 오늘의 실습은 그 리허설이다.
10.1 시스템 경계와 흐름도 라벨링
물 1 L를 깔때기에 부어 플라스크로 흘려보낸다. 깔때기에 처음 물이 없었고 부은 1 L가 전부 빠져나갔다면, 깔때기에 남은 물은 얼마인가. 답은 0 L다. 이 답은 깔때기의 크기나 벽면의 기울기와 무관하다. 들어간 양과 나온 양만 알면 내부 구조를 몰라도 결론이 나온다.
이 하찮은 예제가 물질수지의 뼈대 전부다. 관심 영역을 정해 두고 그 안팎으로 드나드는 질량을 장부에 적는 계산을 물질수지(material balance)라 한다. 그리고 관심 영역을 둘러싸는 가상의 닫힌 면을 시스템 경계(system boundary)라 한다. 경계를 어디에 긋는가에 따라 같은 공정에서도 전혀 다른 식이 나오는데, 이 선택의 자유를 10.3절에서 재순환 문제를 푸는 데 그대로 쓴다.
경계를 그었으면 장부의 계정은 다섯 개다. 들어온 것, 나간 것, 안에서 생긴 것, 안에서 없어진 것, 그리고 그 결과로 쌓인 것이다. 이를 한 줄로 쓰면 일반 수지식을 얻는다.
축적 = 입력 − 출력 + 생성 − 소멸 (10.1)
생성과 소멸은 화학반응의 몫이다. 이 장에서는 반응이 없는 공정만 다루므로 두 항은 0이다. 여기에 조업 조건이 시간이 지나도 변하지 않는 상태, 곧 정상상태(steady state)를 가정하면 축적도 0이 되어 식은 가장 단순한 형태로 줄어든다.
입력 = 출력 (10.2)
10.1.1 흐름도 라벨링: 문제의 절반
서술형 문제를 받으면 식보다 그림이 먼저다. 문장 속에 흩어진 장치와 흐름을 종이 위의 흐름도로 옮기는 작업을 라벨링이라 부르며, 순서는 다음과 같다.
- 문제에 나온 공정마다 이름을 붙인다(혼합기 A, 분리기 B 순으로).
- 모든 스트림에 번호를 붙이고, 유량은 ṁ1처럼 스트림 번호를 첨자로 단다.
- 주어진 값이든 미지수든 전부 흐름도에 적는다(단위를 반드시 포함한다).
- 부피 유량은 질량 유량과 구별되게 표시해 둔다.
마지막 항목이 함정이다. 질량과 달리 부피에는 대부분의 경우 보존 법칙이 없다. 부피 유량이 주어지면 밀도를 곱해 질량 유량으로 환산한 뒤에 수지식에 넣어야 하며, "부피 보존" 식을 세우면 그 시점에서 답이 틀어진다.
미지수를 세는 규칙도 여기서 정해진다. 성분이 C개인 스트림의 독립 미지수는 C개다. 질량분율 C−1개와 총유량 1개다. 분율의 합이 1이므로 마지막 분율은 나머지에서 자동으로 정해지지만, 총유량은 분율만으로는 알 수 없기 때문이다.
예제 10.1-1 설탕물 혼합기
문제 설탕 질량분율 0.20인 설탕물 100 kg/h(스트림 1)와 0.80인 설탕물 50 kg/h(스트림 2)를 혼합기에서 섞는다. 정상상태에서 출구 스트림 3의 유량과 설탕 분율을 구하라.
풀이 전략 성분이 둘(설탕, 물)이므로 독립 수지는 두 개다. 총괄수지와 설탕 수지를 쓰기로 한다. 이 정도 규모는 손계산 영역이다. AI를 부를 일이 아니다.
계산 총괄수지(식 10.2): ṁ3 = 100 + 50 = 150 kg/h. 설탕 수지: 스트림 1이 100 × 0.20 = 20 kg/h, 스트림 2가 50 × 0.80 = 40 kg/h를 들여오므로 출구 설탕 유량은 60 kg/h.
답: ṁ3 = 150 kg/h, x설탕,3 = 60/150 = 0.40
- 단위 체크: 모든 항이 kg/h로 식 (10.2)와 일치
- 대조값: 혼합물 분율은 두 입력 분율 사이에 있어야 한다. 0.20 < 0.40 < 0.80으로 통과
- 극한값: 스트림 2 유량 → 0이면 x설탕,3 → 0.20으로 수렴해야 한다. 식에 0을 대입하면 그대로 재현된다
분석 성분이 C개면 독립 수지도 C개다. 총괄수지를 쓰면 성분 수지 하나를 대신할 수 있을 뿐, 식이 하나 늘어나는 것이 아니다. 이 사실은 다음 절의 자유도 계산과 10.4절의 특이 행렬 에러에 그대로 이어진다. 스트림 2의 분율을 0.5로 바꿔 극한 검증을 한 번 더 해 보라.
스스로 점검 (해답은 권말)
- 성분 3개짜리 스트림 하나의 독립 미지수는 몇 개인가?
- 정상상태이지만 반응이 있는 계라면 식 (10.1)에서 어떤 항이 살아남는가?
10.2 자유도 분석: 풀리는 문제인지 먼저 판정한다
식을 다 세우고 연립을 반쯤 풀고 나서야 "안 풀리는 문제였다"를 아는 것은 가장 늦은 발견이다. 계산을 시작하기 전에 미지수 개수와 독립 방정식 개수를 세어 비교하면 풀리는 문제인지 미리 알 수 있다. 이 차이를 자유도(degree of freedom, DOF)라 한다.
DOF = 미지수 개수 − 독립 방정식 개수 (10.3)
판정은 세 갈래다. 자유도가 0이면 미지수를 유한한 해로 결정할 수 있는 적정지정(well-defined) 문제다. 0보다 크면 정보가 모자라는 과소지정(underspecified), 0보다 작으면 정보가 남거나 서로 모순일 수 있는 과잉지정(overspecified)이다.
| DOF | 판정 | 대응 |
|---|---|---|
| 0 | 적정지정(풀 수 있다) | 풀이 계획 수립으로 진행 |
| > 0 | 과소지정(정보 부족) | 추가 측정·사양 확보, 또는 타당한 가정 도입 |
| < 0 | 과잉지정(정보 과잉) | 중복 정보 통합, 가정 제거, 모순 여부 점검 |
방정식으로 셀 수 있는 것은 성분별 수지만이 아니다. 분리 효율 같은 공정 사양, 조성 사이의 관계식, 문제 서술이 주는 추가 정보도 모두 방정식 한 건으로 친다. 단, 서로 독립일 때만 센다. "들어온 A의 60 %가 스트림 2로 간다"와 "A의 40 %가 스트림 3으로 간다"는 같은 정보의 앞뒷면이므로 한 건이다.
이 판정을 포함한 풀이 절차 전체가 화공양론 교재(Felder & Rousseau)가 정식화한 체계적 풀이 프로토콜이다. 순서는 흐름도 작성 → 라벨링 → 자유도 분석 → 풀이 계획 수립 → 계산이다. 이 다섯 단계는 그 자체로 하나의 알고리즘이며, 이 장의 제목에 있는 "알고리즘화"는 numpy 이전에 여기서 시작한다. 코드로 옮기는 것은 마지막 두 단계뿐이고, 앞의 세 단계는 사람의 몫으로 남는다.
- 문제 정의
- 분해: 경계·라벨링·자유도 분석 (이 장의 주 훈련 블록)
- 프롬프트
- 생성 코드 읽기
- 테스트
- 화공 검증: 질량 보존·대조값·극한값 (이 장의 보조 훈련 블록)
10.2.1 다중 유닛 시스템의 자유도
장치가 여러 개면 유닛마다 자유도 분석을 한 뒤 합산한다. 그리고 두 장치 사이를 잇는 중간 스트림의 변수 개수를 합계에서 뺀다. 중간 스트림은 나가는 장치에서 한 번, 들어가는 장치에서 한 번, 두 번 세어졌기 때문이다. 이렇게 얻은 수가 시스템 전체의 자유도다.
자유도가 0으로 판정되면 풀이 계획은 기계적으로 나온다. 자유도가 0인 유닛(또는 유닛 조합)을 찾아 그 미지수를 먼저 계산하고, 계산된 값을 기지수로 바꿔 나머지 유닛의 자유도를 다시 센다. 어딘가가 새로 0이 되면 거기를 풀고, 구하려는 값이 전부 나올 때까지 반복한다.
예제 10.2-1 분리기의 자유도 분석
문제 A 50 %(질량), B 50 %인 혼합물 100 kg/h가 분리기에 들어간다. 분리기는 들어온 A의 60 %와 B의 50 %를 상부 스트림 2로, 나머지를 하부 스트림 3으로 보낸다. 이 문제가 풀리는지 판정하고, 풀린다면 두 출구의 유량과 A 분율을 구하라.
풀이 전략 먼저 라벨링부터 한다. 미지수는 ṁ2, xA2, ṁ3, xA3의 넷이다. 다음으로 자유도를 세고, 그다음에야 계산이다.
계산 쓸 수 있는 독립 방정식은 성분 수지 2개(A, B)다. 추가 정보는 "A의 60 %가 스트림 2로" 1건과 "B의 50 %가 스트림 2로" 1건으로 합계 2건이다. 40 %/50 %가 스트림 3으로 간다는 서술은 독립 정보가 아니므로 세지 않는다. 따라서 DOF = 4 − 2 − 2 = 0, 적정지정이다.
계산은 짧다. 스트림 2의 A는 0.6 × 50 = 30 kg/h, B는 0.5 × 50 = 25 kg/h. 스트림 3은 나머지인 A 20 kg/h, B 25 kg/h.
답: ṁ2 = 55 kg/h, xA2 = 30/55 = 0.545, ṁ3 = 45 kg/h, xA3 = 20/45 = 0.444
- 단위 체크: 유량 kg/h, 분율 무차원으로 일관
- 대조값: 출구 합 55 + 45 = 100 kg/h = 공급으로 총괄 질량 보존 통과
- 극한값: 분리 사양이 A 100 %/B 0 %라면 스트림 2는 순수 A(xA2 = 1)가 되어야 한다. 식에 대입하면 그대로 나온다
분석 이 예제의 교훈은 계산이 아니라 정보 세기다. 자유도 분석에서 가장 흔한 실수는 같은 정보를 두 번 세거나(과잉으로 오판) 사양을 빠뜨리는 것(과소로 오판)이다. 분리 사양을 "A의 70 %"로 바꿔 결과가 어떻게 움직이는지 다시 계산해 보라.
생각해보기 과잉지정은 항상 나쁜가? 센서가 달린 실제 공정에서는 측정값이 방정식보다 많은 상황이 오히려 기회다. 남는 식에 측정값을 넣어 보면 측정이 서로 맞는지 검사할 수 있다. 이 성질을 공정 진단에 활용할 방법을 두 가지 제안해 보라. (최종 시험 주제 예시의 "물질수지 정합성 검사"가 바로 이것이다.)
스스로 점검 (해답은 권말)
- 유닛 두 개가 중간 스트림 하나(성분 2개)로 연결되어 있다. 유닛별 DOF 합이 3이면 시스템 DOF는 얼마인가?
- "스트림 2와 3의 조성이 같다"는 조건은 자유도 분석에서 무엇으로 세는가?
10.3 재순환과 퍼지: 되돌아오는 흐름의 회계
분리가 한 번에 충분하지 않으면 덜 분리된 흐름을 공급 쪽으로 되돌려 다시 통과시킨다. 이렇게 공정 출구의 일부를 상류로 되돌리는 흐름을 재순환(recycle)이라 한다. 재순환이 붙으면 흐름도에 새 교차점이 두 개 생긴다. 스트림이 갈라지는 지점을 분기점(splitting point), 신선 공급과 재순환 흐름이 만나는 지점을 합류점(recombination point)이라 한다.
스트림 1 ──▶ [합류점] ──▶ ┌────────┐ ──▶ 스트림 2 (상부 생성물)
(신선 공급) ▲ 스트림 4 │ 분리기 │
│ └────────┘
│ │ 스트림 3 (하부)
│ ▼
└──────────── [분기점] ──▶ 스트림 6 (하부 생성물)
스트림 5 (재순환)
재순환 시스템의 회계 규칙은 세 가지다. 첫째, 분기점과 합류점도 자유도 분석에서는 하나의 공정으로 센다. 다른 어디에서도 세어지지 않는 미지수를 갖기 때문이다. 둘째, 분기점에서 갈라진 두 흐름은 조성이 같다. 이 조성 동일 조건이 방정식 1건이 되고, 분기비(얼마를 되돌리는가)가 주어지면 또 1건이 된다. 셋째, 합류점에서는 이런 추가 방정식이 나오지 않는다. 조성이 다른 두 흐름이 섞이므로 출구 조성은 수지를 풀어야 나온다.
둘째 규칙에는 함정이 하나 붙어 있다. 자유도 분석이 끝나기 전에는 갈라진 두 흐름의 조성을 같은 변수로 라벨링하지 마라. 같다고 적어 버리는 순간 그 정보를 이미 써 버린 것이라, 방정식 수를 셀 때 이중으로 세거나 빠뜨리기 쉽다. 미지수는 미지수대로 두고, 정보는 정보대로 세는 것이 안전하다.
풀이 계획에서는 10.1절의 경계 선택이 그대로 쓰인다. 경계를 시스템 전체에 그은 총괄수지(overall balance)를 세우면 재순환 스트림은 경계를 드나들지 않으므로 식에서 아예 사라진다. 재순환의 조성이 미지수일 때 이 요령 하나가 계산량을 크게 줄이며, 재순환 문제는 대개 총괄수지에서 출발하는 것이 좋다.
그림 10-1의 시스템(성분 2개)에 10.2절의 다중 유닛 절차를 적용해 보자. 합류점은 미지수 6 − 수지 2 = 4 DOF, 분리기는 미지수 6 − 수지 2 − 분리 사양 2 = 2 DOF, 분기점은 미지수 6 − 수지 2 − 조성 동일 1 − 분기비 1 = 2 DOF다. 신선 공급이 지정되면 합류점은 4가 아닌 2 DOF가 되고, 세 교차점의 합 2 + 2 + 2에서 중간 스트림 변수 6을 빼면 시스템 자유도는 0이다. 문제는 풀린다. 손으로 풀면 대입이 여러 겹이지만, 10.4절에서 행렬로 한 번에 처리한다.
10.3.1 퍼지: 루프의 배출구
재순환 루프에서 일부를 계 밖으로 뽑아 버리는 흐름을 퍼지(purge)라 한다. 반응 공정에서 원료에 섞여 들어온 비활성 성분은 반응으로도 분리로도 없어지지 않아 루프를 돌며 쌓이는데, 퍼지는 이 축적을 막는 배출구 역할을 한다. 수지 회계의 관점에서 퍼지는 분기점이 하나 늘어나는 것과 같다. 10.4절의 행렬에 행이 몇 줄 더해질 뿐, 새 이론은 필요 없다. 반응이 얽힌 퍼지의 정량 설계는 화공양론 수업의 몫으로 남긴다.
생각해보기 재순환 비율을 올리면 분리는 공짜로 좋아지는가? 직관으로 먼저 답을 적어 두고, 예제 10.4-1을 푼 뒤 돌아와 자신의 답을 채점해 보라.
10.4 연립선형방정식의 행렬 표현과 numpy 풀이
수지식을 코드로 넘기기 전에 미지수 선택부터 손봐야 한다. (총유량, 분율) 쌍을 미지수로 쓰면 식마다 ṁ2xA2 같은 곱이 나타난다. 미지수끼리의 곱은 1차식이 아니므로, 이대로는 이 절의 도구를 쓸 수 없다.
해법은 미지수를 바꿔 잡는 것이다. 스트림 속 한 성분의 질량 유량을 통째로 하나의 미지수로 잡은 것을 성분 유량(component flow rate)이라 한다. 스트림 2의 A 성분 유량을 A2라 쓰면 ṁ2xA2가 곱이 아니라 A2 하나가 되고, 무반응 물질수지는 전부 미지수의 덧셈과 상수배만 남는다. 이런 방정식을 선형(linear) 방정식이라 하며, 여러 개를 묶은 것이 연립선형방정식이다.
선형 연립방정식은 표준형이 있다. 계수만 뽑아 정사각 표로 모은 것을 계수 행렬(coefficient matrix) A, 미지수를 세로로 쌓은 것을 벡터 x, 우변 상수를 벡터 b라 하면 연립 전체가 한 줄이 된다.
Ax = b (10.4)
numpy가 푸는 것이 정확히 이 형태다. 작은 3원 연립으로 손을 먼저 풀어 보자.
import numpy as np
# 연립방정식 (Kreyszig, Advanced Engineering Mathematics 9판, 7.3절 예제)
# x1 - x2 + x3 = 0
# 10*x2 + 25*x3 = 90
# 20*x1 + 10*x2 = 80
A = np.array([[ 1, -1, 1],
[ 0, 10, 25],
[20, 10, 0]])
b = np.array([0, 90, 80])
x = np.linalg.solve(A, b) # 해 벡터
print(x)
[2. 4. 2.]
해가 나왔다. 그런데 이 해가 정말 방정식을 만족하는지 코드로 검증하려다 보면 첫 함정을 만난다.
print(A @ x) # A와 x의 행렬곱 — b와 같아야 한다
print(A @ x == b) # 원소별 비교
print(np.allclose(A @ x, b)) # 허용 오차 안에서의 비교
[2.66453526e-15 9.00000000e+01 8.00000000e+01] [False True True] True
첫 성분은 0이어야 하는데 2.7 × 10−15가 나왔고, 그래서 원소별 비교의 첫 칸이 False다. 컴퓨터의 실수 연산에는 이 정도의 오차가 항상 붙는다. 따라서 수치 해의 등호 검증에는 == 대신 허용 오차 안 비교인 np.allclose를 쓴다. 검증 루틴을 코드로 옮길 때의 표준 관례다.
예제 10.4-1 재순환 분리 공정을 numpy로 풀기
문제 그림 10-1의 공정에서 신선 공급(스트림 1)은 100 kg/h, A 50 %(질량)다. 분리기는 들어온 A의 60 %와 B의 50 %를 스트림 2로 보낸다. 스트림 3의 절반이 재순환된다(분기비 0.5). 상부 생성물(스트림 2)과 하부 생성물(스트림 6)의 유량과 A 분율을 구하라.
풀이 전략 10.3절에서 이 시스템의 자유도가 0임을 확인했으므로 바로 풀이 계획이다. 미지수는 성분 유량 여섯 개 x = [A2, B2, A3, B3, A4, B4]로 잡는다. 재순환(스트림 5)과 하부 생성물(스트림 6)은 분기비 규칙(각각 스트림 3의 절반)으로 후처리하면 되므로 미지수에서 뺀다. 식은 성분 i = A, B에 대해 세 묶음이다.
합류점: i4 − 0.5 i3 = 50 (10.5)
분리 사양: A2 = 0.6 A4, B2 = 0.5 B4 (10.6)
분리기 수지: i2 + i3 = i4 (10.7)
식 여섯 개에 미지수 여섯 개다. 행렬 조립은 AI에게 맡기고, 행별 대조와 검증은 직접 한다.
recycle 있는 분리 공정 물질수지야. 성분 A, B 두 개. 신선 공급 100 kg/h에 A가 50%. 분리기는 들어온 A의 60%를 스트림 2로, B의 50%를 스트림 2로 보내. 스트림 3의 절반이 재순환돼서 공급이랑 합쳐져. 미지수를 성분 유량 [A2, B2, A3, B3, A4, B4] 순서로 잡고 numpy로 풀게 계수 행렬이랑 b 벡터 만들어줘. 각 행이 어느 장치의 어떤 수지인지 한국어 주석 달아줘.
import numpy as np
# 미지수 x = [A2, B2, A3, B3, A4, B4] # 전부 kg/h (성분 유량)
A_mat = np.array([
[0, 0, -0.5, 0, 1, 0 ], # 합류점 A 수지: A4 - 0.5*A3 = 50
[0, 0, 0, -0.5, 0, 1 ], # 합류점 B 수지: B4 - 0.5*B3 = 50
[1, 0, 0, 0, -0.6, 0 ], # 분리 사양 A: A2 = 0.6*A4
[0, 1, 0, 0, 0, -0.5], # 분리 사양 B: B2 = 0.5*B4
[1, 0, 1, 0, -1, 0 ], # 분리기 A 수지: A2 + A3 = A4
[0, 1, 0, 1, 0, -1 ], # 분리기 B 수지: B2 + B3 = B4
])
b = np.array([50, 50, 0, 0, 0, 0]) # kg/h (신선 공급의 성분 유량)
x = np.linalg.solve(A_mat, b)
print(x)
✔ 여섯 행을 식 (10.5)~(10.7)과 한 행씩 대조했다. 분리 사양 행의 −0.6, −0.5는 우변 항을 이항한 부호다. b의 단위는 전부 kg/h로 일관. 미지수 순서가 프롬프트에서 지정한 순서와 일치.
[37.5 33.33333333 25. 33.33333333 62.5 66.66666667]
성분 유량이 나왔으니 문제가 묻는 총유량과 분율로 되돌리는 후처리가 남았다. 하부 생성물은 스트림 3의 절반이다.
A2, B2, A3, B3, A4, B4 = x
m2 = A2 + B2 # kg/h, 상부 생성물 총유량
xA2 = A2 / m2 # 상부 생성물의 A 질량분율
m6 = 0.5 * (A3 + B3) # kg/h, 하부 생성물(스트림 3의 절반)
xA6 = 0.5 * A3 / m6 # 하부 생성물의 A 질량분율
print(m2, xA2) # 70.83..., 0.529...
print(m6, xA6) # 29.16..., 0.428...
print(m2 + m6) # 100.0 — 총괄 질량 보존
print(np.allclose(A_mat @ x, b)) # True — 해가 방정식을 만족
답: ṁ2 = 70.8 kg/h, xA2 = 0.53, ṁ6 = 29.2 kg/h, xA6 = 0.429
- 단위 체크: 계수는 무차원 분율·비율, x와 b는 kg/h이고 식 (10.4) 전체가 kg/h로 일관
- 대조값: 총괄 질량 70.8 + 29.2 = 100 kg/h = 공급. 교과서 손풀이 값(ṁ4 = 129.17 kg/h, xA4 = 0.484)과 대조했다. 코드에서 A4 + B4 = 129.17, A4/(A4+B4) = 0.484로 일치
- 극한값: 합류점 행의 −0.5를 0으로 바꾸면(재순환 차단) ṁ2 = 55 kg/h, xA2 = 0.545로 예제 10.2-1의 무재순환 해가 재현된다
분석 재순환의 효과를 읽어 보자. 하부 생성물의 A 분율은 0.444에서 0.429로 내려갔다. 분리가 좋아졌다. 그러나 그 유량은 45에서 29.2 kg/h로 줄었다. 개선에는 대가가 따르며, 물질수지는 그 대가를 정량으로 보여 주는 회계 장부다. 노트북에서 분기비를 0.7로 바꿔 재실행하고, 실습에서는 학번별 분리 사양으로 같은 절차를 반복하라.
스스로 점검 (해답은 권말)
A @ x == b의 원소별 비교가 False를 낼 수 있는 이유를 한 문장으로 설명하라.- 코드 10-3의 다섯째 행
[1, 0, 1, 0, -1, 0]이 표현하는 물질수지를 한국어로 말하라. - 미지수 6개에 독립 방정식 5개짜리 시스템을
np.linalg.solve에 넣으면 무슨 일이 생기는가?
실습 10: 도구⑦ 물질수지 solver (75분)
이번 실습은 75분 타이머로 진행하는 mini practical이며, 종료 후 공개 지목 구술이 있다. 학번별 파라미터 생성기가 배부한 재순환 공정 문제를 받아, 자유도 분석에서 검증 기록까지 한 번에 완주한다.
준비물
- 스타터 repo(GitHub Classroom 발급)와 학번별 문제 파일. 스트림 표와 분리 사양이 학번마다 다르다.
- numpy 동작 확인: 터미널에서
python -c "import numpy; print(numpy.__version__)"이 버전을 출력해야 한다. - 재사용할 이전 도구: 5주차 도구④ 단위환산 + 물성 조회기의 환산 함수. 문제 파일의 스트림 일부는 lb/h 단위로 주어진다. 행렬에 넣기 전에 kg/h로 정규화해야 한다.
과제
- 실습 10.1 스타터 repo를 클론하고 본인 학번의 문제 파일을 열어 스트림 표를 확인하라. (성공 시 장치 목록과 스트림 표, 분리 사양이 보인다) 확인 즉시 첫 커밋을 남겨라.
- 실습 10.2 AI를 부르기 전에, 손으로 흐름도를 라벨링하고 자유도 분석을 수행해 README에 표로 기록하라. (완료 기준: 미지수 목록·방정식 목록·DOF 값이 표에 있고 DOF = 0) 이 단계는 사람 몫이다. 여기서 틀리면 뒤의 코드 전부가 틀린다.
- 실습 10.3 도구④의 환산 함수를 import해 문제 파일의 모든 유량을 kg/h로 정규화하는 코드를 작성하라. (완료 기준: 정규화된 스트림 표가 출력되고 단위 주석이 달려 있다) 커밋하라.
- 실습 10.4 AI에게 행렬 조립 함수
build_system()을 생성시키고, 행별 주석을 식과 대조한 뒤np.linalg.solve로 풀어라. (완료 기준: 해 벡터가 출력되고, 행 대조 결과를 README에 한 줄 남겼다) 커밋하라. - 실습 10.5 검증 루틴 3종(총괄 질량 보존,
np.allclose잔차 검사, 분기비 → 0 극한에서 무재순환 해 재현)을 코드로 붙여라. (완료 기준:pytest test_solver.py3개 통과) 커밋하라.
완성 기준 통과/재도전 2단계로 판정한다.
pytest test_solver.py3개 통과(질량 보존 · 무재순환 극한 · 특이 행렬 입력 시 rank 진단 메시지 출력)- README에 흐름도 라벨링, DOF 표, 검증 루틴 3종의 결과 기록
- 학번별 파라미터로 계산한 최종 답(유량·분율) 표
- 커밋 3회 이상. 단계 완료마다 남긴 커밋 이력이 증거다
막혔는가?
- 행렬이 안 세워지면: "이 공정의 미지수를 성분 유량으로 잡으면 몇 개인지 세고, 장치별로 쓸 수 있는 독립 방정식을 표로 만들어 줘. 코드는 아직 쓰지 마."라고 물어라. 분해부터 다시 하는 것이다.
Singular matrix가 나면: 에러 메시지 전문을 붙여넣고 "이 행렬의 rank를 확인하는 코드와, 어느 행들이 서로 종속인지 찾는 방법을 알려 줘."라고 물어라.- 해가 음수 유량이면: "물질수지 해에 음수 유량이 나왔다. 계수 부호가 틀렸을 후보 행 3개를 짚고, 각 행을 원래 수지식과 대조하는 절차를 알려 줘."라고 물어라.
퇴실 전 기록 오늘 만든 것·막힌 것·틀린 것 1건을 위키에 커밋해야 퇴실한다. 이번 주는 위키 마일스톤② 집계 주간이다. 5주차 이후의 커밋 리듬, evergreen 승격, 타 과목 연결이 자동 집계된다. 몰아치기 커밋은 이력에 그대로 드러난다.
요약
- S 10-1 일반 수지식: 축적 = 입력 − 출력 + 생성 − 소멸. 무반응·정상상태면 입력 = 출력.
- S 10-2 DOF = 미지수 − 독립 방정식. 0 적정 / 양수 과소지정 / 음수 과잉지정. 다중 유닛은 유닛별 합산 후 중간 스트림 변수를 뺀다.
- S 10-3 성분 유량을 미지수로 잡으면 물질수지는 Ax = b가 되고,
np.linalg.solve(A, b)로 푼 뒤np.allclose(A @ x, b)로 검증한다. - S 10-4 프롬프트 패턴: 미지수 목록과 순서를 지정하고, 행별로 "어느 장치의 어떤 수지인지" 주석을 요구하라. 총괄수지는 방정식이 아니라 검증에 쓴다.
| 항목 | (총유량, 질량분율) | 성분 유량 |
|---|---|---|
| 식의 형태 | ṁ·x 곱이 있어 비선형 | 전부 1차식(선형) |
| numpy 풀이 | 비선형 solver가 필요 | np.linalg.solve 한 번 |
| 물리적 직관 | 분율이 바로 보인다 | 후처리로 분율 환산 필요 |
| 권장 용도 | 손계산·작은 문제 | 코드화·다중 유닛·재순환 |
이 장에서 강해진 것은 AI 협업 사이클의 ② 분해다. 경계 긋기·라벨링·자유도 분석은 AI가 대신해 주지 않는 사람의 단계이고, 이 단계가 어긋나면 numpy도 특이 행렬 에러를 돌려준다. 도구 사다리는 ④ 물성 조회 → ⑤ 증기압 → ⑥ T-xy → ⑦ 물질수지 solver까지 올라왔다. 처음으로 "장치 여러 개"를 다루는 도구가 생겼다.
용어 정리
- 물질수지(material balance)
- 시스템 경계를 드나드는 질량을 일반 수지식으로 회계하는 계산.
- 시스템 경계(system boundary)
- 수지를 세울 관심 영역을 둘러싸는 가상의 닫힌 면.
- 정상상태(steady state)
- 시간이 지나도 계 내부 상태가 변하지 않아 축적이 0인 조업 상태.
- 자유도(degree of freedom, DOF)
- 미지수 개수에서 독립 방정식 개수를 뺀 값. 문제의 해결 가능성 판정 기준.
- 적정지정(well-defined)
- 자유도가 0이어서 유한한 해를 결정할 수 있는 문제.
- 과소지정(underspecified)
- 자유도가 양수인 경우. 정보가 부족해 해가 정해지지 않는 문제.
- 과잉지정(overspecified)
- 자유도가 음수인 경우. 정보가 남거나 서로 모순일 수 있는 문제.
- 재순환(recycle)
- 공정 출구의 일부를 상류로 되돌려 다시 처리하는 흐름.
- 분기점(splitting point)
- 한 스트림이 같은 조성의 여러 흐름으로 갈라지는 지점.
- 합류점(recombination point)
- 신선 공급과 재순환처럼 조성이 다른 흐름들이 섞이는 지점.
- 퍼지(purge)
- 재순환 루프에서 일부를 계 밖으로 뽑아 성분 축적을 막는 배출 흐름.
- 총괄수지(overall balance)
- 경계를 시스템 전체에 그어 세운 수지. 재순환 스트림이 식에서 사라진다.
- 성분 유량(component flow rate)
- 스트림 속 한 성분의 질량 유량을 통째로 잡은 미지수. 수지식을 선형으로 만든다.
- 선형(linear)
- 미지수의 덧셈과 상수배만으로 이루어진 방정식의 성질.
- 계수 행렬(coefficient matrix)
- 연립선형방정식의 계수를 모아 만든 행렬 A.
- 특이 행렬(singular matrix)
- 행들이 서로 종속이어서 역행렬이 없고
np.linalg.solve가 실패하는 행렬.
연습문제
Q군: 개념·읽기 (AI 없이 풀 것)
- Q10.1 다음 코드의 출력을 실행 없이 예측하라.
import numpy as np A = np.array([[1, 1], [1, -1]]) b = np.array([10, 2]) print(np.linalg.solve(A, b)) - Q10.2 코드 10-3의 셋째 행
[1, 0, 0, 0, -0.6, 0]이 표현하는 물질수지를 한국어 두 문장으로 설명하라(EiPE). 부호 −0.6이 왜 음수인지 반드시 포함하라. - Q10.3 증발기와 열교환기가 중간 스트림 하나(성분 1개)로 연결된 시스템이 있다. 증발기의 DOF가 2, 열교환기의 DOF가 1일 때 시스템 전체의 DOF를 구하고 판정하라.
P군: 제작·계산 (AI 사용 전제)
- P10.1 도구⑦을 확장해 분기비를 인자로 받게 하고, 분기비를 0부터 0.9까지 훑으며 하부 생성물의 A 분율과 유량을 그래프로 그려라(matplotlib). 분기비 = 0 극한이 무재순환 해와 일치함을 그래프에 표시하고 README에 검증 기록을 남겨라.
- P10.2 예제 10.4-1의 행렬을 일부러 풀 수 없게 만드는 방법을 두 가지 제시하고, 각각을 코드로 재현해
np.linalg.matrix_rank로 진단하는 리포트를 작성하라. 각 방법이 자유도 관점에서 무엇을 망가뜨렸는지 한 줄씩 설명하라. - P10.3 (도전) 장치 3개와 재순환·퍼지가 있는 공정의 측정 데이터(모든 스트림의 유량·조성)가 주어졌다고 하자. 과잉지정 상태를 역이용해, 측정값이 물질수지를 만족하는지 잔차로 검사하고 어긋난 스트림 후보를 지목하는 정합성 검사기를 설계·구현하라. 최종 시험 주제 예시 ②와 동형의 문제다.
위키 기록 과제 ① 이번 장 핵심 1페이지(#seed): F&R 프로토콜 다섯 단계와 행렬 조립 레시피를 본인의 말로 정리하라. ② "내가 틀렸던 것" 1건: Singular matrix든 부호 실수든, 에러 스크린샷과 원인을 함께 남겨라. ③ 타 과목 연결 1건: 화공양론의 재순환 단원 문제 하나를 도구⑦로 검산한 기록을 링크하라. 이번 주 의미 커밋 1회 이상이 채점 지표다.
11장 유체역학 계산: 배관과 마찰
이번 주의 도구: 도구⑧ 배관 압력강하 계산기
다음 상황을 생각해 보자. 어느 공장에서 반응기 냉각수 라인을 100 m 증설하기로 했다. 펌프 카탈로그에는 사양이 수십 종인데, 어느 것을 골라야 하는지는 결국 숫자 하나에 달려 있다. 물이 그 배관을 지나는 동안 마찰로 잃어버릴 압력이다.
이 값을 구하는 절차는 유체역학 교과서에 이미 있다. 유속으로 레이놀즈 수를 계산하고, Moody 선도에서 마찰계수를 눈으로 읽고, Darcy-Weisbach 식에 대입한다. 문제는 조건이 바뀔 때마다 이 3단계를 처음부터 되풀이해야 한다는 데 있다. 관 지름 후보가 5개, 유량 시나리오가 4개면 손계산 20번이다.
이번 주에 만드는 도구⑧은 이 반복을 함수 호출 한 번으로 줄인다. 그리고 이 장의 마찰계수에는 AI가 유난히 자주 틀리는 지점이 있다. 두 규약이 정확히 4배 차이로 갈리는 곳이다. 검증 루틴 3종을 실전에서 쓰게 된다.
- 레이놀즈 수로 층류와 난류를 판별하는 기준을 도입한다
- Darcy-Weisbach 식으로 배관 압력강하 계산을 형식화한다
- Colebrook 식·명시적 근사식·Moody 선도의 관계를 정리한다
- Darcy와 Fanning, 두 마찰계수 규약의 4배 함정을 검증 루틴으로 잡는다
- 도구⑧ 배관 압력강하 계산기를 만들고 고장 코드를 디버깅한다
이 장을 마치면 다음을 할 수 있다.
- 유체 물성과 배관 조건으로 레이놀즈 수를 계산하고 층류/난류를 판별할 수 있다.
- Darcy-Weisbach 식의 각 항을 단위까지 설명하고 압력강하를 계산할 수 있다.
- 흐름 영역에 맞는 마찰계수 식(64/Re, Colebrook, 명시적 근사)을 골라 쓸 수 있다.
- 생성 코드가 어느 마찰계수 규약을 쓰는지 코드만 읽고 판별할 수 있다.
- Moody 선도를 코드로 그리고 계산 결과를 그 위에 점으로 표시할 수 있다.
- 검증 루틴 3종으로 고장 난 압력강하 코드의 버그를 찾아 고칠 수 있다.
이번 주 실습에서는 §11.2~11.4의 식과 함수로 도구⑧ 배관 압력강하 계산기를 만든다(75분 타이머, 공개 지목 구술). 이번 실습은 시험2 전 마지막 mini practical이다. 다음 주 실습 슬롯이 곧 시험2이고, 규정 브리핑은 이번 주 이론 세션에서 한다(시험2 규정 전문은 12장 표 12-1). 특히 실습 11.6의 고장 코드 디버깅은 시험2의 문항 유형 그대로다.
11.1 레이놀즈 수: 층류와 난류
수도꼭지를 아주 조금만 열면 물줄기가 유리 막대처럼 매끈하게 떨어진다. 조금 더 열면 어느 순간 물줄기가 흐트러지고 표면이 거칠어진다. 같은 물, 같은 꼭지인데 흐름의 상태가 다르다.
앞의 매끈한 흐름처럼 유체가 층을 이루어 가지런히 미끄러지는 상태를 층류(laminar flow)라 한다. 뒤의 거친 흐름처럼 크고 작은 소용돌이가 뒤섞이며 나아가는 상태를 난류(turbulent flow)라 한다. 배관 안에서도 같은 두 상태가 나타나는데, 마찰로 잃는 압력의 계산법이 상태에 따라 완전히 달라진다. 그래서 압력강하 계산의 첫 단계는 언제나 "지금 흐름이 어느 쪽인가"라는 판별이다.
판별 기준은 무차원수 하나로 요약된다. 관 내 흐름에서 레이놀즈 수(Reynolds number, Re)를 다음과 같이 정의한다.
Re = ρ·v·D / μ (11.1)
여기서 ρ는 유체 밀도[kg/m³], v는 평균 유속[m/s], D는 관 내경[m], μ는 점도[Pa·s]다. 분자 ρvD는 유체가 하던 대로 밀고 나가려는 관성의 크기에, 분모 μ는 흐트러짐을 눌러 가지런히 만들려는 점성의 크기에 대응한다. 관성이 이기면 난류, 점성이 이기면 층류다.
정의했으면 단위부터 검산해 보자(검증 루틴 1종인 단위 체크다). Pa·s = kg/(m·s)이므로 식 (11.1)의 단위는
(kg/m³) · (m/s) · (m) / (kg/(m·s)) = 1
분자와 분모가 남김없이 지워진다. Re는 단위가 없는 무차원수다. 계산 결과에 단위가 남는다면 식을 잘못 옮긴 것이다.
경계값은 어디인가. 관 내 흐름에서는 Re < 2320이면 층류로, Re > 2320이면 난류로 판정한다. 임계값으로 2100을 쓰는 문헌도 있다. 실제 전이는 칼로 자르듯 일어나지 않기 때문에 문헌마다 관례가 조금씩 다르다. 이 책은 2320 기준으로 통일하고, 코드에 판별을 넣을 때는 어느 값을 썼는지 주석으로 남긴다.
예제 11.1-1 냉각수 흐름의 판별
25 °C의 물이 내경 D = 0.05 m인 배관을 흐른다. 평균 유속이 (a) 2.0 m/s, (b) 0.02 m/s일 때 각각 레이놀즈 수를 구하고 흐름 상태를 판별하라. 물성은 ρ = 994.6 kg/m³, μ = 8.93×10⁻⁴ Pa·s로 한다(§11.4의 물성 상관식 계산값).
식 (11.1)에 대입만 하면 되는 산술이므로 AI 없이 직접 계산한다(협업 사이클 ①~②만 해당). 손계산으로 기준값을 먼저 만들어 두어야 나중에 코드가 틀렸을 때 알아볼 수 있다.
(a) Re = 994.6 × 2.0 × 0.05 / (8.93×10⁻⁴) ≈ 1.11×10⁵. 2320보다 두 자릿수 크므로 난류다.
(b) 유속이 (a)의 1/100이므로 Re도 1/100이다. Re ≈ 1.11×10³ < 2320, 층류다.
답: (a) Re ≈ 1.1×10⁵, 난류 (b) Re ≈ 1.1×10³, 층류
- 단위: 본문에서 확인한 대로 모든 단위가 소거된다. Re는 무차원이다.
- 대조값: 자릿수 암산 1000 × 2 × 0.05 / 0.001 ≈ 10⁵로, 계산기 결과와 오더가 일치한다.
- 극한값: v → 0이면 Re → 0(정지 유체는 층류 극한), μ가 클수록(끈적할수록) Re가 작아져 층류 쪽이다. 물리 직관과 부합한다.
분석 물처럼 점도가 낮은 유체가 공정 규모의 관을 실용적인 유속으로 흐르면 Re는 대개 10⁴~10⁶에 놓인다. 난류가 기본값이다. 층류가 되려면 (b)처럼 유속이 cm/s 수준으로 느리거나, 관이 아주 가늘거나, 유체가 훨씬 끈적해야 한다. What-if: 같은 조건에서 점도가 물의 1000배인 시럽이라면 (a)는 어느 쪽이 되는지 계산해 보라.
- 같은 유속·같은 관에서 수온을 올리면 물의 Re는 커지는가, 작아지는가? (물의 점도는 온도가 오르면 감소한다)
- Re = 2500인 흐름을 층류식으로 계산하면 어떤 위험이 있는지 한 문장으로 설명하라.
11.2 Darcy-Weisbach 식: 마찰로 잃는 압력
유체가 관 벽을 스치며 흐르면 벽에는 흐름을 거스르는 마찰력이 걸린다. 이 마찰을 이기느라 흐름 방향을 따라 압력이 조금씩 낮아지는데, 길이 L인 구간에서 잃는 압력을 압력강하(pressure drop) Δp라 한다. 펌프는 정확히 이 값만큼을 보충해야 하므로, 압력강하 계산이 그대로 펌프 사양 계산이 된다.
관 내 흐름의 압력강하는 Darcy-Weisbach 식으로 계산한다.
Δp = λ · (L/D) · ρv2/2 (11.2)
식을 한 항씩 해부해 보자. ρv²/2는 흐르는 유체의 운동에너지를 압력 단위로 나타낸 양으로, 동압(dynamic pressure)이라 부른다. 단위를 확인하면 (kg/m³)·(m²/s²) = kg/(m·s²) = Pa로, 정확히 압력이다. L/D는 관이 지름의 몇 배만큼 긴가를 나타내는 무차원 기하 인자다. 길수록 마찰 구간이 길어 손실이 커지고, 굵을수록 벽의 영향이 옅어져 손실이 작아진다.
남는 것은 λ 하나다. 벽 마찰의 세기를 담는 이 무차원 계수를 마찰계수(friction factor)라 하고, 식 (11.2) 규약의 것을 특히 Darcy 마찰계수(Darcy friction factor)라 부른다. 식 (11.2)의 다른 항은 전부 문제 조건에서 바로 나오므로, 이 장의 나머지는 사실상 λ를 구하는 이야기다.
그런데 문헌에는 마찰계수가 두 종류 있다. 벽 전단응력을 동압으로 나눈 비를 Fanning 마찰계수(Fanning friction factor) fF라 하는데, 같은 물리를 다르게 정규화했을 뿐이라 두 계수는 정확히 4배 관계다.
λ = 4·fF (11.3)
층류 마찰계수가 Darcy 규약에서 64/Re, Fanning 규약에서 16/Re로 적히는 것이 이 4배의 흔적이다. 어느 쪽도 틀린 규약이 아니지만, 둘을 섞어 쓰면 계산이 어긋난다. Fanning 값 fF를 식 (11.2)에 넣으면 압력강하가 4배 작게 나오고, 그 값으로 고른 펌프는 4배 모자란다. 문헌·웹·AI 출력에서 마찰계수를 만나면 "층류식이 64/Re인가 16/Re인가"부터 확인한다. §11.4에서 AI가 정확히 이 지점에서 엇갈리는 사례를 본다.
- 식 (11.2)에서 유속을 그대로 두고 관 지름만 2배로 하면 L/D와 Re는 각각 어느 방향으로 변하는가?
- 어느 웹 문서의 마찰계수 상관식이 층류 극한에서 16/Re로 수렴한다. 이 상관식의 값을 식 (11.2)에 바로 넣어도 되는가?
11.3 마찰계수: Colebrook 식에서 Moody 선도까지
11.3.1 층류: 반복이 필요 없는 영역
층류(Re < 2320)의 마찰계수는 Poiseuille의 결과로 닫힌 식이 알려져 있다.
λ = 64 / Re (11.4)
식 (11.4)를 식 (11.2)에 대입하면 Δp = 32·μ·L·v/D²을 얻는다. 층류의 압력강하는 유속에 정비례하고, 밀도와 무관하며, 점도에 비례한다. 관성이 아니라 점성이 지배하는 영역답다.
11.3.2 난류: 조도가 등장한다
난류가 되면 사정이 복잡해진다. λ가 Re뿐 아니라 관 벽이 얼마나 거친가에도 의존하기 때문이다. 벽 요철의 대표 높이 ε[m]을 절대조도(absolute roughness), 그것을 관 지름으로 나눈 무차원비 ε/D를 상대조도(relative roughness)라 한다. 조도 값은 관의 재질과 상태에 따라 다르며 유체역학 교재나 제조사 자료의 표에서 조회한다(이 장의 문제에서는 조건으로 준다).
난류 마찰계수의 표준 관계식은 Colebrook-White 식(1937)이다.
λ = 1 / [ 2·log₁₀( 2.51/(Re·√λ) + 0.27·ε/D ) ]2 (11.5)
식 (11.5)를 잘 보면 이상한 점이 있다. 구하려는 λ가 우변 안에도 들어 있다. 이렇게 미지수를 좌변에 홀로 남기도록 대수적으로 풀 수 없는 식을 암시적 식(implicit equation)이라 하고, 답은 반복 계산으로 얻는다. 방법은 10장의 연립방정식 풀이보다 오히려 단순하다. 적당한 초기값을 우변에 넣어 새 λ를 얻고, 그 값을 다시 우변에 넣는 일을 값이 더 변하지 않을 때까지 되풀이한다. 이를 고정점 반복(fixed-point iteration)이라 한다.
import math
Re = 1.11e5 # 무차원 (예제 11.1-1의 난류 조건)
rel_rough = 0.001 # 무차원 (상대조도 ε/D)
lam = 0.02 # 초기 추측값 — Moody 선도 세로축의 중간쯤
for i in range(6):
rhs = 2.51 / (Re * math.sqrt(lam)) + 0.27 * rel_rough
lam = 1.0 / (2 * math.log10(rhs))**2
print(f"{i+1}회: lam = {lam:.6f}")
실행하면 네 번째 반복부터 소수 여섯째 자리까지 값이 멈춘다.
1회: lam = 0.022057
2회: lam = 0.021955
3회: lam = 0.021960
4회: lam = 0.021960
5회: lam = 0.021960
6회: lam = 0.021960
초기값이 아무 값이나 되는 것은 아니다. lam = 0.0으로 시작하면 √λ = 0으로 나누게 되어 다음 에러가 그대로 난다.
Traceback (most recent call last):
File "colebrook.py", line 8, in <module>
rhs = 2.51 / (Re * math.sqrt(lam)) + 0.27 * rel_rough
ZeroDivisionError: float division by zero
초기값은 물리적으로 그럴듯한 값이어야 한다. Moody 선도에서 난류 마찰계수는 대략 0.01~0.1 사이에 놓이므로, 그 중간인 0.02쯤이 안전한 출발점이다. 반복 계산의 초기값을 답이 있을 법한 범위에서 고르는 습관은 10장의 fsolve 초기 추측과 같은 원리다.
11.3.3 반복을 피하는 길: 명시적 근사식
매번 반복문을 돌리는 대신, 정확도를 조금 양보하고 한 번의 대입으로 끝나는 명시적 근사식(explicit approximation)을 쓸 수 있다. 매끈한 관(ε/D ≈ 0), 2320 < Re < 10⁵ 범위에서는 Blasius 식이 좋은 근사를 준다.
λ = 0.3164 · Re−0.25 (11.6)
조도까지 포함해 Re > 2320 전 영역을 덮는 것은 Swamee-Jain 식(1976)이다.
λ = 0.25 / [ log₁₀( ε/(3.7·D) + 5.75/Re0.9 ) ]2 (11.7)
식 (11.7)의 ε/(3.7·D)는 식 (11.5)의 0.27·ε/D와 같은 항이다(1/3.7 ≈ 0.27). 문헌마다 표기가 다를 뿐이다. 그리고 두 식 모두 로그의 밑과 상수가 한 세트다. 8장의 Antoine 상수에서 본 것과 같은 함정으로, 식 (11.7)을 자연로그 ln으로 쓰는 문헌은 상수도 0.25 대신 1.325를 쓴다. 식을 옮기며 로그 밑을 바꾸면 상수도 함께 바뀌어야 한다.
| 식 | 적용 영역 | 조도 | 형태 |
|---|---|---|---|
| (11.4) 64/Re | Re < 2320 | 무관 | 명시적 |
| (11.5) Colebrook-White | Re > 2320 | 포함 | 암시적(반복 필요) |
| (11.6) Blasius | 2320 < Re < 10⁵, 매끈한 관 | 불포함 | 명시적 |
| (11.7) Swamee-Jain | Re > 2320 전 영역 | 포함 | 명시적 |
도구⑧에는 어떤 식을 넣을까. 층류는 식 (11.4)로 확정이고, 난류는 식 (11.7)을 기본으로 쓰되 식 (11.5)를 반복으로 푼 값과 1% 이내로 일치하는지 대조한다. 예제 11.1-1의 난류 조건(Re = 1.11×10⁵, ε/D = 0.001)에서 식 (11.7)은 0.0221, 코드 11-1의 수렴값은 0.0220이다. 0.8% 차이로, 조도 값 자체의 불확실성보다 훨씬 작다.
11.3.4 Moody 선도: 식들의 지도
지금까지의 식을 전부 한 장의 그래프에 겹쳐 그린 것이 Moody 선도(Moody diagram, 1944)다. 가로축 Re, 세로축 λ를 모두 로그 눈금으로 잡고, 왼쪽에 층류 직선(64/Re)을, 오른쪽에 상대조도별 난류 곡선 가족을 그린다. 컴퓨터 이전 시대에는 이 선도에서 눈으로 λ를 읽었고, 지금도 계산값의 자릿수가 맞는지 한눈에 확인하는 지도로 쓴다.
로그-로그 그래프는 matplotlib에서 plt.plot 대신 plt.loglog를 쓰면 된다. 층류 직선만 먼저 그려 보자.
import numpy as np
import matplotlib.pyplot as plt
Re = np.linspace(500, 2320, 50) # 층류 영역 (무차원)
plt.loglog(Re, 64 / Re) # 두 축 모두 로그 눈금
plt.xlabel("Re")
plt.ylabel("Darcy friction factor")
plt.grid(True, which="both")
plt.show()
로그-로그 눈금에서 64/Re는 기울기 −1의 직선으로 나타난다. 난류 곡선 가족과 계산점 표시는 실습 11에서 직접 완성한다.
생각해보기 Re = 2320 바로 근처(이를테면 2200~2500)에서는 어느 식도 자신 있게 쓸 수 없다. 경계 지점인 Re = 2320에서 식 (11.4)는 0.0276, 식 (11.7)은 (ε/D = 0.001 기준) 0.0495를 준다. 값이 두 배 가까이 다르다. 실제 설계에서 이 구간의 흐름을 만나면 어떻게 대처할지 방법을 두 가지 제안해 보라. (힌트: 안전 여유라는 관점, 그리고 조업 조건을 바꾼다는 관점)
- Colebrook 식이 "암시적"이라는 말의 뜻을 한 문장으로 설명하라.
- 식 (11.6)을 Re = 10⁶인 흐름에 써도 되는가? 이유를 말하라.
11.4 AI 협업: 마찰계수 함수와 4배 함정
- 문제 정의
- 분해
- 프롬프트
- 생성 코드 읽기
- 테스트
- 화공 검증
도구⑧의 중심은 "Re와 상대조도를 받아 λ를 돌려주는 함수" 하나다. §11.2에서 규약이 두 가지임을 알았으므로, 프롬프트에 규약을 못 박는 것이 이번 장의 프롬프트 패턴이다.
배관 마찰계수를 계산하는 파이썬 함수를 만들어 줘. 입력은 레이놀즈 수와 상대조도(ε/D), 출력은 Darcy 마찰계수야. Re가 2320 미만이면 층류식 64/Re를 쓰고, 그 이상이면 Swamee-Jain 식을 써. Fanning이 아니라 Darcy 마찰계수라는 걸 docstring에 명시하고, Re가 0 이하이면 ValueError를 내게 해 줘. 주석은 한국어로.
import numpy as np
def darcy_friction_factor(Re, rel_rough=0.0):
"""Darcy 마찰계수를 반환한다 (Fanning의 4배 규약).
Re : 레이놀즈 수 (무차원)
rel_rough : 상대조도 ε/D (무차원), 기본값 0 = 매끈한 관
"""
if Re <= 0:
raise ValueError("Re는 양수여야 한다")
if Re < 2320: # 층류 (Poiseuille)
return 64.0 / Re
# 난류: Swamee-Jain 명시적 근사 (Re > 2320 전 영역)
arg = rel_rough / 3.7 + 5.75 / Re**0.9
return 0.25 / np.log10(arg)**2
✔ 로그 밑 확인: np.log10이므로 상수 0.25와 옳은 세트다. 층류 앵커: Re = 1000 입력 → 0.064 반환, λ·Re = 64 성립. 난류 대조: Re = 1.11×10⁵, ε/D = 0.001 → 0.02213, 코드 11-1의 Colebrook 수렴값 0.02196과 0.8% 차. 규약이 docstring에 명시됐고 Re ≤ 0 예외도 요구대로다.
생각해보기 실행해 보지 않고 함수 본문만 읽어서 어느 규약인지 판별할 수 있는 단서를 두 가지 찾아보라. (층류 분기의 상수, 그리고 난류식의 형태가 각각 단서가 된다)
검증을 마친 함수가 생겼으니 전체 계산을 조립하자. 물성과 조건을 대입하는 정도의 스크립트는 직접 쓴다. 무엇을 AI에게 시키고 무엇을 직접 할지 가르는 감각도 훈련의 일부다.
예제 11.4-1 냉각수 배관의 압력강하
25 °C의 물이 내경 D = 0.05 m, 길이 L = 100 m인 배관을 평균 유속 v = 2.0 m/s로 흐른다. 절대조도는 ε = 0.05 mm로 주어졌다. 압력강하를 구하라.
마찰계수는 검증을 마친 코드 11-3을 재사용한다. 물성은 사람이 책임진다. 아래 코드의 물 밀도·점도 상관식(코퍼스 pycse 수록, Polymath 문제집 8.8)을 쓰고, 값이 그럴듯한지는 검증 단계에서 도구④ 단위환산 + 물성 조회기로 교차 확인한다. 사이클 ①~③은 §11.4 앞부분에서 끝났으므로 여기서는 ④~⑥을 수행한다.
import numpy as np
from friction import darcy_friction_factor # 코드 11-3 (검증 완료)
T = 298.15 # K (25 °C)
# 물의 밀도·점도 상관식 (T는 K 단위 크기, 출처: pycse/Polymath 8.8)
rho = 46.048 + 9.418*T - 0.0329*T**2 + 4.882e-5*T**3 - 2.895e-8*T**4 # kg/m^3
mu = np.exp(-10.547 + 541.69 / (T - 144.53)) # Pa·s
D, L, v = 0.05, 100.0, 2.0 # m, m, m/s
eps = 0.05e-3 # m (절대조도 0.05 mm — mm→m 환산)
Re = rho * v * D / mu # 무차원
lam = darcy_friction_factor(Re, eps / D) # 무차원
dp = lam * (L / D) * 0.5 * rho * v**2 # Pa
print(f"rho={rho:.1f} kg/m3, mu={mu:.2e} Pa.s, Re={Re:.2e}")
print(f"lam={lam:.4f}, dp={dp/1000:.1f} kPa")
실행 결과는 다음과 같다.
rho=994.6 kg/m3, mu=8.93e-04 Pa.s, Re=1.11e+05
lam=0.0221, dp=88.0 kPa
답: Δp ≈ 88.0 kPa(= 0.880 bar)
- 단위: 입력이 전부 SI(ε는 mm → m 환산 확인), ρv²/2 = Pa이므로 Δp도 Pa다. 식 (11.2)와 일치한다.
- 대조값: 같은 조건을 Colebrook 반복(코드 11-1)으로 풀면 λ = 0.0220, Δp = 87.3 kPa로, 0.8% 이내 일치한다. 물성도 도구④ 조회값과 대조하라.
- 극한값: ε/D → 0으로 놓으면 λ = 0.0175, 매끈한 관의 Blasius 식 (11.6)이 주는 0.0173과 1% 이내다. 매끈한 관 극한이 맞물린다.
분석 계산 자체는 세 줄이고 지면 대부분이 검증에 쓰였다. 이 비율이 정상에 가깝다. 유속을 2배(4.0 m/s)로 올려 다시 돌리면 Δp는 335 kPa, 약 3.8배가 된다. 속도 제곱의 4배에 조금 못 미치는 이유는 Re가 커지면서 λ가 살짝 줄기 때문이다. What-if: 조도를 10배(0.5 mm)로 바꿔 재실행하고, λ와 Δp가 얼마나 커지는지, Moody 선도에서 계산점이 어느 곡선으로 옮겨 가는지 확인해 보라.
동시수강 중인 유체역학 과목과의 관계를 짚어 두자. 유체역학 강의가 이 식들의 유도와 물리를 담당한다면, 이 장은 같은 식을 "계산 가능한 형태"로 옮겨 도구로 굳히는 쪽을 담당한다. 종이 위의 Moody 선도에서 눈으로 λ를 읽는 훈련과, 그 선도를 코드로 그려 계산점을 얹는 훈련은 같은 내용의 양면이다. 이론 수업에서 새 상관식을 만나면 도구⑧에 분기 하나로 추가해 보라(도구를 함수로 만들어 둔 이유가 그것이다).
실습 11: 도구⑧ 배관 압력강하 계산기 (75분)
공개 지목 구술을 포함한다. 이번 실습은 시험2 전 마지막 리허설이고, 마지막 단계의 고장 코드 디버깅은 시험2 문항 유형 그대로다.
준비물
- Python + numpy/matplotlib 환경(5주차와 동일). 확인:
python -c "import numpy, matplotlib"가 아무 출력 없이 끝나면 정상. - 재사용: 도구④ 단위환산 + 물성 조회기의 물성 함수(물의 ρ, μ). CoolProp을 썼다면 그대로 import한다.
- 스타터 repo의
broken_pipe.py(실습 11.6 디버깅 연습용 고장 코드, 코드 11-5).
과제
- 실습 11.1 repo에
friction.py를 만들고, §11.4의 프롬프트 패턴으로 마찰계수 함수를 생성해 넣어라. (완료 확인: Re = 1000에서 0.064를 반환한다) 커밋하라. - 실습 11.2 층류 앵커(λ·Re = 64)와 난류 대조(Colebrook 반복값 대비 1% 이내)를 검사하는 테스트 2개를 AI에게 요청해
test_friction.py로 저장하고 pytest로 돌려라. (완료 확인: 2개 통과) 커밋하라. - 실습 11.3
pressure_drop(D, L, v, rho, mu, eps)함수를 만들어라. 물성은 도구④에서 가져온다. (완료 확인: 예제 11.4-1 조건에서 88.0 kPa ± 1%) - 실습 11.4 Moody 선도를 그려라: 층류 직선 + 상대조도 4종(0, 10⁻⁴, 10⁻³, 10⁻²)의 Swamee-Jain 곡선, 축은 loglog. (완료 확인:
moody.png가 저장된다) - 실습 11.5 예제 11.4-1의 계산점(Re, λ)을 선도 위에 별표로 표시하고, README에 그림과 검증 3종 기록을 넣어라. (완료 확인: 계산점이 ε/D = 10⁻³ 곡선 위에 놓인다) 커밋하라.
- 실습 11.6
broken_pipe.py에는 버그가 세 개 심어져 있다. 검증 루틴 3종을 무기로 전부 찾아 고치고, 각 버그가 어느 루틴에 걸렸는지 README에 한 줄씩 기록하라. (완료 확인: 예제 11.4-1 조건에서 옳은 값이 나온다) 커밋하라.
import math
def pressure_drop(D_mm, L, v, rho, mu, eps_mm):
"""배관 압력강하 [Pa]를 반환한다."""
D = D_mm # 관 내경
Re = rho * v * D / mu # 레이놀즈 수
if Re < 2320:
lam = 16.0 / Re # 층류 마찰계수
else:
arg = eps_mm / (3.7 * D) + 5.75 / Re**0.9
lam = 0.25 / math.log(arg)**2 # Swamee-Jain
return lam * (L / D) * 0.5 * rho * v**2
힌트를 하나만 준다. 예제 11.4-1 조건을 넣으면 88,033 Pa 대신 14.7 Pa가 나온다. 몇 배나 틀렸는지가 단서다.
완성 기준
- pytest 테스트 2개 이상 통과 (실습 11.2)
- 예제 11.4-1 조건에서 88.0 kPa ± 1% 재현 (실습 11.3)
moody.png+ 계산점 표시 (실습 11.4~11.5)- README에 검증 3종 기록 + 디버깅 리포트 3줄 (실습 11.6)
- 의미 있는 커밋 ≥ 4
판정은 통과/재도전 2단계다. 재도전은 다음 실습 전까지 허용된다.
막혔는가? 마찰계수 값이 이상할 때
AI에게 이렇게 물어라: "이 함수에 Re=1000을 넣으면 0.016이 나와. Darcy 마찰계수라면 얼마가 나와야 하고, 왜 다른지 후보 원인 3개를 말해 줘."
막혔는가? Moody 선도가 그려지지 않을 때
AI에게 이렇게 물어라: "matplotlib loglog로 여러 곡선을 겹쳐 그리는 최소 예제를 보여 줘. x는 2320부터 1e8까지 로그 간격 200점이야."
막혔는가? broken_pipe.py에서 에러 없이 값만 틀릴 때
AI에게 이렇게 물어라: "이 코드와 정답값(88.0 kPa)을 줄게. 내 결과가 몇 배 다른지부터 계산하고, 그 배율을 만들 수 있는 버그 후보를 나열해 줘." 배율이 단서다. 4배 차이가 나면 규약을, 1000배 차이가 나면 단위 환산을 의심하라.
퇴실 전 기록 오늘 만든 것·막힌 것·틀린 것 1건을 위키에 커밋해야 퇴실이다. 특히 broken_pipe.py에서 마지막까지 못 찾던 버그가 좋은 소재다.
요약
- S 11-1 Re = ρvD/μ (무차원). Re < 2320 층류, Re > 2320 난류. 이 책의 기준이며, 임계값은 문헌에 따라 다를 수 있다.
- S 11-2 Δp = λ(L/D)ρv²/2 (Darcy-Weisbach). λ = 4fF, 층류 앵커 λ·Re = 64.
- S 11-3 λ는 흐름 영역에 따라 고른다. 층류는 64/Re, 난류는 Swamee-Jain 식 (11.7)을 기본으로 하고 Colebrook 식 (11.5)의 반복 해와 1% 이내로 대조한다.
- S 11-4 프롬프트 패턴은 "규약을 못 박아라"이다. 출력이 Darcy인지 Fanning인지, 로그 밑이 무엇인지 프롬프트에 명시하고, 받은 코드는 층류 앵커로 검수한다.
| 층류 | 난류 | |
|---|---|---|
| 판별 | Re < 2320 | Re > 2320 |
| 마찰계수 | 64/Re (닫힌 식) | Colebrook(암시적) 또는 명시적 근사, ε/D에 의존 |
| 조도의 영향 | 없음 | 있음 |
| Δp의 유속 의존 | 정비례(∝ v) | 대략 제곱(예제 11.4-1에서 2배 유속 → 3.8배) |
| 공정 현장에서 | 드묾(저속·세관·고점도) | 기본값 |
이번 장의 수확은 식보다 경험이다. 예외 한 줄 없이 조용히 4배 틀리는 코드를, λ·Re = 64라는 앵커 하나로 잡아냈다. AI 협업 사이클의 ⑤ 테스트와 ⑥ 화공 검증이 이제 반사 신경에 가까워졌을 것이다. 도구 사다리는 ④ 물성 → ⑤ 증기압 → ⑥ T-xy → ⑦ 물질수지 → ⑧ 압력강하까지 올라왔다. 남은 것은 이 도구들을 시간 압박 아래에서 꺼내 쓰는 훈련이고, 그 자리가 시험이다.
용어 정리
- 층류(laminar flow)
- 유체가 층을 이루어 가지런히 미끄러지는 흐름 상태. 관 내 흐름에서 Re < 2320.
- 난류(turbulent flow)
- 소용돌이가 뒤섞이며 진행하는 흐름 상태. 공정 배관의 기본값.
- 레이놀즈 수(Reynolds number, Re)
- 관성과 점성의 비를 나타내는 무차원수, ρvD/μ. 층류/난류 판별 기준.
- 압력강하(pressure drop)
- 마찰 때문에 배관 구간에서 잃는 압력, Δp. 펌프가 보충해야 할 값.
- 마찰계수(friction factor)
- 벽 마찰의 세기를 담는 무차원 계수. Darcy 규약(λ)과 Fanning 규약(fF = λ/4)이 있다.
- Darcy 마찰계수(Darcy friction factor)
- 식 (11.2)에 그대로 들어가는 마찰계수. 층류에서 64/Re.
- Fanning 마찰계수(Fanning friction factor)
- 벽 전단응력을 동압으로 나눈 마찰계수. 층류에서 16/Re, Darcy의 1/4.
- 절대조도(absolute roughness)
- 관 벽 요철의 대표 높이 ε[m]. 재질·상태에 따라 표로 조회한다.
- 상대조도(relative roughness)
- 절대조도를 관 지름으로 나눈 무차원비 ε/D. 난류 마찰계수를 결정하는 인자.
- 암시적 식(implicit equation)
- 미지수를 좌변에 홀로 남기도록 정리할 수 없어 반복 계산이 필요한 식. Colebrook 식이 그 예다.
- 명시적 근사식(explicit approximation)
- 한 번의 대입으로 값이 나오도록 만든 근사식. Blasius, Swamee-Jain 식.
- 고정점 반복(fixed-point iteration)
- 초기값을 식의 우변에 넣어 얻은 값을 다시 우변에 넣기를 수렴할 때까지 되풀이하는 반복법.
- Moody 선도(Moody diagram)
- Re–λ 평면(로그-로그)에 층류 직선과 상대조도별 난류 곡선을 함께 그린 마찰계수 지도.
연습문제
Q군: 개념·읽기 (AI 없이 풀 것, 시험1 대비)
- Q11.1 다음 코드가 인쇄할 세 줄을 실행하지 말고 예측하라.
rho, mu = 994.6, 8.93e-4 # kg/m^3, Pa·s D = 0.05 # m for v in [0.01, 0.2, 2.0]: # m/s Re = rho * v * D / mu state = "laminar" if Re < 2320 else "turbulent" print(f"v={v}: Re={Re:.0f} -> {state}") - Q11.2 코드 11-3의
darcy_friction_factor의 목적과 동작을 한국어 2~3문장으로 설명하라(EiPE).rel_rough의 기본값 0.0은 물리적으로 어떤 관을 뜻하는가? - Q11.3 AI가 낸 다음 풀이를 채점하고 틀린 곳을 교정하라: "Re = 1500인 층류 흐름이므로 f = 16/1500 = 0.0107이고, 이것을 Δp = f·(L/D)·ρv²/2에 넣어 계산하면 된다." 검증 루틴 3종 가운데 어느 것이 이 오류를 잡는가?
P군: 제작·계산 (AI 사용 전제, 시험2·최종 시험 대비)
- P11.1 예제 11.4-1과 같은 배관에서, 학번 끝 두 자리를 XY라 할 때 유속 v = 1.X m/s, 조도 ε = 0.0Y mm로 바꿔 압력강하를 계산하고 검증 3종을 README에 기록하라.
- P11.2 열교환기에 물 2.5 L/s를 길이 100 m의 매끈한 관으로 보내야 한다. 25 °C에서 압력강하가 103 kPa를 넘지 않게 하는 최소 관 내경을 구하라. 지름이 유속과 Re를 함께 바꾸므로
fsolve로 푼다(10장 참조). 대조값: 같은 문제를 Fanning 규약 상관식으로 푼 문헌 풀이의 답은 0.0390 m다. 1% 안팎에서 일치해야 한다. - P11.3 도구⑧에 "경계 영역 리포트"를 붙여라: 2200 ≤ Re ≤ 2500이면 층류식과 난류식의 λ를 모두 계산해 경고 문구와 함께 큰 값을 쓰도록 하라. 그리고 이 계산기를 티 나지 않게 틀리게 만드는 방법을 두 가지 제시하고, 각각을 잡아내는 pytest 테스트를 작성하라.
위키 기록 과제 이번 주 의미 커밋 ≥ 1이 채점 지표다. 다음 3항을 커밋하라. ① 이 장 핵심 1페이지 정리(#seed): 식 (11.1)·(11.2)와 마찰계수 선택 기준을 본인의 말로. ② "내가 틀렸던 것" 1건: broken_pipe.py에서 마지막까지 못 찾은 버그와 잡은 순간의 화면 캡처. ③ 타 과목 연결 1건: 유체역학 강의 노트의 Moody 선도와 내 moody.png를 나란히 놓고 다른 점 한 가지.
12장 공정 데이터 다루기
이번 주의 실전: 시험2(90분 현장 실기 리허설)
다음 상황을 생각해 보자. 학부연구생으로 들어간 실험실에서 첫 임무를 받았다. 파일럿 설비의 차압식 유량계를 보정한 열두 번의 측정 기록이 담긴 CSV 파일을 건네받으며 "보정 상수 구해서 내일까지 보고해 주세요"라는 말을 들었다. 스프레드시트를 열어 평균부터 내 보니 어딘가 이상하다. 오후 측정의 평균 유량이 오전의 다섯 배다. 설비가 오후에 다섯 배 힘차게 돌았을 리는 없다.
데이터 어딘가에 거짓말이 숨어 있고, 그것을 찾아내는 것은 감이 아니라 절차다. 이 장은 그 절차를 만든다. 표를 읽는 pandas, 선을 긋는 최소자승 회귀, 거짓말을 찾는 이상치 탐지다. 그리고 이번 주 실습은 이 셋을 90분 안에 조립하는 시험2다.
이 장에서 다루는 것
- 표 형태의 공정 데이터를 pandas로 읽고 요약하는 기본기를 익힌다
- 최소자승 회귀의 직관을 잔차로 세우고
np.polyfit코드와 잇는다 - 평균과 기울기를 무너뜨리는 이상치를 탐지하고 조사하는 절차를 세운다
- 시험2(20점, AI 필수, 90분 현장 실기)의 규정과 시간 배분 전략을 정리한다
학습목표
이 장을 마치면 다음을 할 수 있다.
- CSV 공정 데이터를 pandas로 읽고
head·info·describe세 명령으로 상태를 점검할 수 있다. - 조건 선택과 그룹 집계로 데이터에서 원하는 부분만 추릴 수 있다.
- 최소자승 회귀가 무엇을 최소화하는지 잔차로 설명하고,
np.polyfit으로 직선을 피팅할 수 있다. - 요약 통계와 잔차를 근거로 이상치 후보를 찾아내고, 지우기 전에 조사·기록하는 절차를 적용할 수 있다.
- 시험2 규정(빈 repo 첫 커밋, 90분 내 커밋 4회 이상)에 맞는 시간 배분 계획을 세울 수 있다.
이번 주 실습 2시간은 도구 제작이 아니라 시험2(20점, AI 필수, 90분 현장 실기) 그 자체다. 형식·인프라·규정이 최종 시험과 같은 3/4 스케일 리허설이므로, 이 장의 12.1~12.3절이 시험 범위의 마지막 조각이고 실습 12가 시험장 매뉴얼이다.
12.1 표를 다루는 근육: pandas
5장부터 지금까지 만든 도구들은 입력이 숫자 몇 개였다. 온도 하나, 압력 하나, 조성 두어 개. 그러나 도입부의 보정 기록처럼 현장에서 만나는 데이터는 행 단위로 쌓이는 표다. 표를 다루는 파이썬의 표준 도구가 pandas 라이브러리다.
12.1.1 읽기: 파일에서 표로
도입부의 측정 기록을 지면과 동일하게 재현하기 위해, 데이터 파일부터 코드로 만들어 두자. 실제 현장에서는 계측 시스템이 이런 파일을 내보낸다. (이 데이터는 교재용으로 만든 가상 측정값이다.)
from pathlib import Path
csv_text = """run,session,dP_kPa,Q_m3h
1,am,2,1.19
2,am,4,1.72
3,am,6,2.06
4,am,8,2.43
5,am,10,2.67
6,am,14,3.19
7,pm,18,3.58
8,pm,22,4.01
9,pm,26,43.3
10,pm,30,4.64
11,pm,36,
12,pm,40,5.40
"""
Path("flow_calib.csv").write_text(csv_text, encoding="utf-8")
열 이름 dP_kPa(차압, kPa)와 Q_m3h(유량, m³/h)에 단위를 박아 넣었다. 여섯 달 뒤 파일만 열어 본 사람도 단위를 알 수 있다. 단위 체크 루틴의 데이터 버전이다. 이제 읽어 보자.
import pandas as pd
df = pd.read_csv("flow_calib.csv") # CSV → 표
print(df.head()) # 앞 5행만 표시
실행 결과는 다음과 같다.
run session dP_kPa Q_m3h
0 1 am 2 1.19
1 2 am 4 1.72
2 3 am 6 2.06
3 4 am 8 2.43
4 5 am 10 2.67
지금 화면에 뜬 것처럼 행과 열로 이루어진 표를 담는 pandas의 자료구조를 데이터프레임(DataFrame)이라 한다. 행 하나가 측정 한 번, 열 하나가 변수 하나다. 리스트가 값의 나열이라면 데이터프레임은 이름표가 달린 열들의 묶음이고, 그 이름표로 열을 꺼내 쓴다.
12.1.2 점검 3연타: head·info·describe
head()가 보여 준 다섯 행이 전부가 아니다. 파일을 받으면 세 가지를 연달아 확인한다.
df.info() # 열별 자료형과 채워진 칸 수
print(df["Q_m3h"].describe()) # 유량 열의 통계 요약
print(df.isna().sum()) # 열별 결측 칸 개수
info()의 출력에서 자료형과 채워진 칸 수를 읽을 수 있다.
RangeIndex: 12 entries, 0 to 11
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 run 12 non-null int64
1 session 12 non-null str
2 dP_kPa 12 non-null int64
3 Q_m3h 11 non-null float64
…
Q_m3h 열만 11 non-null이다. 열두 행 중 한 칸이 비어 있다는 뜻이고, 이렇게 기록되지 않은 칸을 결측값(missing value)이라 한다. pandas는 결측을 NaN으로 표시하며, df.isna().sum()으로 열별 개수를 셀 수 있다.
이어서 describe()의 출력을 보자.
count 11.000000
mean 6.744545
std 12.189858
min 1.190000
25% 2.245000
50% 3.190000
75% 4.325000
max 43.300000
max 43.3이 눈에 걸린다. 75% 지점이 4.33인데 최댓값은 그 열 배다. 아직 아무것도 지우지 않는다. 지금은 표시만 해 두고, 처분은 12.3절에서 절차대로 결정한다.
12.1.3 선택과 집계
표 전체가 아니라 일부만 필요할 때가 더 많다. 대괄호에 열 이름을 넣으면 열 하나를 얻고, 조건식을 넣으면 조건을 만족하는 행만 남는다.
q = df["Q_m3h"] # 열 하나 선택 (m³/h)
high = df[df["dP_kPa"] > 20] # 차압 20 kPa 초과 행만
print(len(high)) # 몇 행이 남았는가
print(df.groupby("session")["Q_m3h"].mean()) # 세션별 평균 유량
df["dP_kPa"] > 20은 행마다 참/거짓을 매기고, 그 결과를 다시 대괄호에 넣으면 참인 행 다섯 개만 걸러진다. 마지막 줄처럼 데이터를 그룹으로 나눠 그룹별 요약 통계를 내는 조작을 집계(aggregation)라 한다. 출력은 다음과 같다.
session
am 2.210
pm 12.186
Name: Q_m3h, dtype: float64
도입부의 위화감이 숫자로 나타났다. 오전 평균 2.21, 오후 평균 12.19다. 설비가 오후에 다섯 배 힘차게 돈 것이 아니라, 43.3 한 점이 오후 평균을 끌어올렸다. 평균은 이상치 한 점에 이렇게 약하다.
이 정도 점검은 AI에게 통째로 맡겨도 된다. 다만 결과를 그대로 믿는 대신, 눈으로 센 값과 대조한다.
flow_calib.csv를 pandas로 읽어서 크기, 열별 자료형, 결측값 개수, 숫자 열의 기초 통계를 한 번에 요약해 주는 파이썬 함수를 짜 줘. 주석은 한국어로 달아 줘.
import pandas as pd
def summarize(path):
"""CSV 파일을 읽어 상태 요약을 출력하고 표를 반환한다."""
df = pd.read_csv(path)
print("크기:", df.shape) # (행 수, 열 수)
print(df.dtypes) # 열별 자료형
print(df.isna().sum()) # 열별 결측 개수
print(df.describe()) # 숫자 열 통계 요약
return df
df = summarize("flow_calib.csv")
✔ 실행 결과 크기 (12, 4), 결측 Q_m3h 1건, max 43.3으로, 파일을 직접 열어 센 값과 일치한다. describe()가 문자열 열 session을 빼고 숫자 열만 요약하는 것도 확인했다. 반환값을 df로 받아 두어 이후 분석에 재사용한다.
스스로 점검
df[df["dP_kPa"] > 20]이 반환하는 것은 행의 부분집합인가, 열의 부분집합인가?info()와describe()중 결측값 개수를 알 수 있는 것은 어느 쪽인가?- 출력 예측:
df.groupby("session")["dP_kPa"].max()는 무엇을 출력하는가?
12.2 최소자승 회귀: 가장 덜 틀리는 직선
12.2.1 잔차와 최소자승의 원리
측정점이 두 개면 자를 대고 이으면 된다. 그러나 열한 개의 점이 정확히 한 직선 위에 놓이는 일은 없다. 그렇다면 "가장 덜 틀리는" 직선은 무엇인가.
직선 y = ax + b를 그었을 때, i번째 측정값 yi와 직선이 예측한 값의 차이를 잔차(residual)라 한다: ri = yi − (axi + b). 잔차를 그냥 더하면 양수와 음수가 상쇄되어 엉터리 직선도 합이 0에 가까울 수 있다. 그래서 잔차를 제곱해 더한 값을 기준으로 삼는다.
S(a, b) = Σi [yi − (axi + b)]² (12.1)
식 (12.1)의 S를 최소로 만드는 a와 b를 택하는 방법을 최소자승법(method of least squares)이라 한다. 제곱은 두 가지 일을 한다. 부호를 없애고, 크게 벗어난 점에 제곱으로 커지는 벌점을 준다. 잔차 2의 벌점은 4지만 잔차 10의 벌점은 100이다. 이 두 번째 성질이 어떤 부작용을 일으키는지 12.3절에서 다시 만난다.
S를 a와 b로 각각 미분해 0으로 놓으면 닫힌 형태의 해가 나온다. 그 계산은 np.polyfit이 대신하므로, 사람이 할 일은 결과가 물리적으로 말이 되는지 검증하는 것이다.
12.2.2 np.polyfit으로 피팅하기
보정 대상 유량계의 사양서는 유량과 차압 사이에 다음 관계를 명시한다고 하자.
Q = k √(ΔP) (12.2)
√(ΔP)를 새 변수 x로 놓으면 식 (12.2)는 Q = kx가 되고, 원점을 지나는 직선이다. 곡선 관계를 직선으로 펴는 이런 변수 변환은 회귀의 기본 기술이다. 사양서의 설계값은 k = 0.85 m³ h−1 kPa−1/2이고, 우리 일은 측정 데이터로 이 값을 확인하는 것이다.
예제 12.2-1 보정 상수 피팅: 검증이 먼저 경고한다
flow_calib.csv의 유효한 11개 측정으로 식 (12.2)의 보정 상수 k와 절편을 구하고, 결정계수와 함께 보고하라. 12.1절에서 표시해 둔 43.3은 아직 건드리지 않는다. 모든 데이터를 넣었을 때 무슨 일이 생기는지 보기 위해서다.
결측 행의 처리와 √ 변환은 판단이 필요하므로 프롬프트에 직접 명시하고, 피팅 코드는 AI에게 맡긴다. 단위와 반환값 형식까지 지정한다(사이클 ①~③).
flow_calib.csv의 dP_kPa와 Q_m3h로 Q = k·√ΔP의 보정 상수를 구하고 싶어. √ΔP를 x로 놓고 1차 다항식을 피팅해서 기울기, 절편, R²를 반환하는 함수를 만들어 줘. 결측 행은 빼고, 주석은 한국어로, 단위도 주석에 적어 줘.
import numpy as np
import pandas as pd
def fit_calibration(df):
"""√ΔP에 대한 Q의 1차 피팅. (기울기, 절편, R²)를 반환한다."""
d = df.dropna(subset=["Q_m3h"]) # 결측 행 제외
x = np.sqrt(d["dP_kPa"].to_numpy()) # kPa^0.5
y = d["Q_m3h"].to_numpy() # m³/h
k, b = np.polyfit(x, y, 1) # 1차 피팅: [기울기, 절편]
y_fit = np.polyval((k, b), x) # 피팅 직선의 예측값
ss_res = np.sum((y - y_fit) ** 2) # 잔차 제곱합
ss_tot = np.sum((y - np.mean(y)) ** 2)
return k, b, 1 - ss_res / ss_tot
✔ dropna가 Q_m3h 열 기준으로만 행을 빼는 것, polyfit의 반환 순서가 [기울기, 절편]인 것을 확인했다. R² 계산식은 12.2.3절의 식 (12.3)과 대조했다.
실행해 보자.
df = pd.read_csv("flow_calib.csv")
k, b, r2 = fit_calibration(df)
print(f"k = {k:.3f}, b = {b:.3f}, R² = {r2:.3f}")
k = 3.019, b = -4.628, R² = 0.147
답: k = 3.019 m³ h−1 kPa−1/2, 절편 −4.628 m³/h. 코드는 에러 없이 돌았다. 이제 이 답이 말이 되는지가 문제다.
검증 루틴 3종
- 단위: x가 kPa1/2, y가 m³/h이므로 기울기 k의 단위는 m³ h−1 kPa−1/2로, 사양서와 같은 단위. 통과.
- 대조값: 사양서 설계값 0.85 대비 계산값 3.019로, 3.5배 차이. 실패.
- 극한값: ΔP = 0에서 Q = b = −4.6 m³/h. 차압이 없는데 음수 유량이다. 물리적으로 불가능. 실패.
분석 코드에는 버그가 없다. polyfit은 시킨 일(제곱합 최소화)을 정확히 했고, 잔차 제곱의 벌점 규칙 때문에 43.3 한 점이 직선을 자기 쪽으로 끌어당겼을 뿐이다. 검증 루틴이 잡아낸 것은 코드 결함이 아니라 데이터 결함이다. 이 구분이 이 예제의 교훈이다. What-if: 43.3을 4.33으로 바꿔 재실행하면 세 검증이 어떻게 달라지는지 미리 예측해 보고 12.3절에서 확인하라.
12.2.3 결정계수와 잔차 플롯
피팅이 데이터의 산포를 얼마나 설명하는지 요약하는 지표가 결정계수(coefficient of determination) R²다.
R² = 1 − SSres / SStot (12.3)
SSres는 잔차 제곱합, SStot은 평균 둘레의 총 제곱합이다. 직선이 산포를 전부 설명하면 SSres = 0이 되어 R² = 1이고, 평균보다 나을 게 없으면 0에 가깝다. 예제 12.2-1의 0.147은 "이 직선은 데이터의 15%도 설명하지 못한다"는 뜻이다.
거꾸로 R²가 높다고 모델이 옳다는 보장도 없다. 잔차를 x에 대해 그렸을 때 아무 패턴이 없어야 하며, 잔차가 곡선을 그리면 직선 모델 자체가 틀렸다는 신호다. 숫자만으로는 감이 오지 않으니 그림을 그려 보자.
import matplotlib.pyplot as plt
d = df.dropna(subset=["Q_m3h"])
x = np.sqrt(d["dP_kPa"].to_numpy()) # kPa^0.5
y = d["Q_m3h"].to_numpy() # m³/h
plt.plot(x, y, "o", label="측정값")
xs = np.linspace(0, 6.5) # kPa^0.5
plt.plot(xs, np.polyval((k, b), xs), "-", label="피팅 직선")
plt.xlabel("√ΔP [kPa^0.5]")
plt.ylabel("Q [m³/h]")
plt.legend()
plt.show()
열 점이 한 직선 부근에 가지런히 모여 있고, 한 점만 하늘에 떠 있다. 피팅 직선은 그 점에 끌려 올라가 나머지 점 대부분의 위를 지나간다. 그림 한 장이 R² = 0.147의 사정을 전부 설명한다.
생각해보기
어떤 피팅에서 R² = 0.99가 나왔는데, 잔차를 x에 대해 그렸더니 완만한 곡선 패턴이 보인다. 데이터와 모델 중 무엇을 먼저 의심해야 하는가? 확인하는 방법을 두 가지 제안하라.
스스로 점검
- 식 (12.1)에서 잔차를 제곱하는 두 가지 이유는 무엇인가?
np.polyfit(x, y, 1)의 반환값 순서는 무엇인가?- R² = 1이 되려면 잔차들이 어떤 조건을 만족해야 하는가?
12.3 이상치: 지우기 전에 조사한다
12.3.1 세 가지 근원
나머지 데이터가 이루는 패턴에서 크게 벗어난 점을 이상치(outlier)라 한다. 이상치를 방치하면 예제 12.2-1처럼 결과 전체가 왜곡된다. 그런데 조사 없이 지우는 것도 같은 크기의 실수다. 어느 쪽인지 판단하려면 이상치가 어디서 오는지부터 알아야 한다.
첫째 근원은 기록과 입력의 실수다. 현장 기록지의 4.33이 전산 입력에서 43.3이 되는 식으로, 소수점 한 칸이 값을 열 배로 만든다. 둘째는 센서와 계측의 고장이다. 특정 시간대나 특정 장비의 값만 몰려서 튀면 이쪽을 의심한다.
셋째 근원인 진짜 물리 현상이 문제다. 예상 밖의 조건에서 실제로 일어난 일이 이상치로 보일 수 있고, 이것을 기계적으로 지우는 습관은 새로운 현상의 발견을 지우는 습관이다. 그래서 원칙은 하나로 고정한다. 지우기 전에 조사한다.
12.3.2 잔차로 후보 찾기
조사하려면 먼저 후보를 골라야 한다. describe()의 min/max 훑기와 그림(코드 12-8)이 1차 검사이고, 정량적 기준은 피팅의 잔차에서 나온다.
res = y - np.polyval((k, b), x) # 잔차 (m³/h)
print(np.round(res, 2))
sigma = np.std(res) # 잔차 표준편차
print("잔차 표준편차:", round(sigma, 2))
suspect = d[np.abs(res) > 3 * sigma] # 3σ 초과 후보
print(suspect)
[ 1.55 0.31 -0.71 -1.48 -2.25 -3.48 -4.6 -5.52 32.53 -7.27 -9.07]
잔차 표준편차: 10.73
run session dP_kPa Q_m3h
8 9 pm 26 43.3
잔차 크기가 잔차 표준편차의 3배를 넘는 점을 의심 후보로 본다. 이 책의 작업 규칙이다. run 9가 걸렸다. 그런데 숫자를 자세히 보면 아슬아슬하다. 후보의 잔차 32.53에 대해 경계값 3σ = 32.20으로, 겨우 넘었다.
왜 이렇게 아슬아슬한가. 이상치 자신이 표준편차 계산에 들어가 σ를 부풀렸기 때문이다. 극단적인 이상치일수록 자신을 가려 주는 경계를 스스로 키우므로, 기계적 규칙은 후보 선별까지만 쓰고 최종 판단은 조사에 맡긴다.
12.3.3 조사, 그리고 수정
이 장의 시나리오에서 run 9의 현장 기록지에는 4.33이 적혀 있었다고 하자. 전산 입력에서 소수점이 밀린 것으로, 근원 ①(입력 실수)로 확정되었다. 원인이 확정되었으므로 원본값으로 수정하는 것이 정당하다. 원인을 밝히지 못했다면 수정은 불가능하고, 제거하되 무엇을 왜 몇 건 지웠는지 README에 남긴다(시험의 검증 증빙과 같은 형식이다).
예제 12.3-1 이상치 수정과 재피팅
run 9의 Q_m3h를 기록지 원본값 4.33 m³/h로 수정하고 재피팅하여 보정 상수 k를 확정하라.
어느 행을 어떤 근거로 고치는가는 판단의 영역이므로 두 줄짜리 수정 코드는 직접 쓴다. 재피팅은 예제 12.2-1의 fit_calibration을 그대로 재사용한다.
df_fix = df.copy() # 원본은 남겨 둔다
df_fix.loc[df_fix["run"] == 9, "Q_m3h"] = 4.33 # 기록지 원본값 (m³/h)
k2, b2, r2 = fit_calibration(df_fix)
print(f"k = {k2:.3f}, b = {b2:.4f}, R² = {r2:.4f}")
k = 0.852, b = -0.0069, R² = 0.9998
답: k = 0.852 m³ h−1 kPa−1/2.
검증 루틴 3종
- 단위: 변환·피팅 구조가 그대로이므로 k의 단위 역시 m³ h−1 kPa−1/2. 통과.
- 대조값: 사양서 설계값 0.85 대비 0.852로, 0.3% 이내. 통과.
- 극한값: ΔP = 0에서 Q = −0.007 m³/h ≈ 0. 차압이 없으면 흐름도 없다는 물리와 부합. 통과.
분석 한 점을 고쳤을 뿐인데 k는 3.019에서 0.852로, R²는 0.147에서 0.9998로 바뀌었다. 열한 점 중 한 점이 결과를 지배했다는 뜻이고, 수정 전후를 판정해 준 것은 같은 검증 루틴 3종이었다. 원본 df를 남기고 copy()로 고친 것도 관례다. 무엇을 바꿨는지 추적할 수 있어야 증빙이 된다. What-if: 코드 12-9의 경계를 3σ에서 2σ로 바꿔 정상 점이 후보로 걸려 나오는지 확인해 보라.
생각해보기
열두 번의 측정 중 두 번이 같은 방향으로 튀었고, 센서 점검 결과는 정상이었다. 이 두 점을 지워도 되는가? 판단을 내리기 위해 추가로 필요한 정보 두 가지를 제안하라.
스스로 점검
- 이상치의 세 근원 중 삭제가 가장 위험한 것은 어느 것이며, 왜 그런가?
- 3σ 규칙이 극단적인 이상치를 오히려 놓칠 수 있는 이유를 한 문장으로 설명할 수 있는가?
12.4 데이터 검증 습관: 루틴 3종의 데이터 버전
3장에서 도입한 검증 루틴 3종은 계산 결과만이 아니라 데이터 작업의 모든 단계에 적용된다. 단위 체크는 열 이름에 단위를 넣는 것(dP_kPa, Q_m3h)과 읽은 직후의 자료형 확인으로 나타난다. 단위 행 오염은 dtypes에서 걸렸다. 대조값 체크는 describe()의 min/max를 설비의 조업 상식과 대조하고, 피팅 계수를 사양서·문헌값과 대조하는 일이다. 극한값 체크는 절편의 물리적 의미(ΔP = 0이면 Q = 0)를 캐묻는 일이었다.
회귀식에도 유효범위가 있다. 이 장의 보정식은 ΔP = 2~40 kPa의 데이터로 만들어졌으므로 그 밖은 외삽이다. 8장에서 Antoine 상수를 유효 온도범위 밖으로 외삽했을 때 생긴 오차를 보았다. 같은 규칙이 데이터 피팅에 그대로 적용된다. 회귀식의 유효범위는 데이터의 범위다.
여기에 데이터 특유의 습관 하나를 더한다. 행을 없애거나 고치는 모든 처리의 앞뒤에서 행 수를 찍는 것이다.
n0 = len(df)
df2 = df.dropna(subset=["Q_m3h"]) # 결측 행 제외
print(f"결측 제외: {n0}행 → {len(df2)}행") # 12행 → 11행
한 줄짜리 확인이지만, 12.3절의 AI 경고 사례에서 조용한 2행 손실을 잡아낸 것이 바로 이 습관이다. 정리하면 이 장의 검증 습관은 네 가지다. 읽은 직후 3연타, 열 이름에 단위, 처리 전후 행 수 비교, 피팅 후 검증 3종이다.
- 문제 정의
- 분해
- 프롬프트
- 생성 코드 읽기
- 테스트
- 화공 검증
시험2는 이 사이클 전체를 90분 안에 도는 시험이다. 이제 그 규정과 전략으로 넘어가자.
실습 12: 시험2 (90분 현장 실기, 20점)
이번 주 실습 시간에는 만들 도구가 없다. 실습 그 자체가 시험2이고, 시험2는 최종 시험과 인프라·규정이 같은 3/4 스케일 리허설이다. 여기서 미리 실수를 겪어 두면 최종 시험에서 같은 실수를 줄일 수 있다.
준비물
- 본인 노트북(전원 어댑터 포함). 시험은 유선랜 전산실습실에서 치른다
- Claude Code·게이트웨이 가상 키·GitHub 로그인이 살아 있는 환경 (전날 점검)
- 본인 위키. 시험2와 최종 시험에서 본인 위키 참조는 허용이며 활용도 가점 대상이다
- 재사용할 이전 도구: 도구④~⑧ repo. 특히 이 장의 회귀·이상치 코드는 시험 주제 풀과 직결된다
규정
| 항목 | 내용 |
|---|---|
| 배점·형식 | 20점, AI 필수, 90분 현장 실기 |
| 주제 | 당일 공개 + 학번별 파라미터 |
| 과제 구성 | 소형 도구 제작 + 고장 코드 디버깅 |
| 커밋 | 빈 repo 첫 커밋, 90분 내 커밋 ≥4 |
| 채점 | 자동 스모크 테스트 12점 + 과정 8점(커밋 타임라인·세션 로그 정합성) |
| 선언문 | AI 사용내역 선언문 1쪽 첨부(미첨부 시 부정행위 처리) |
| 위상 | 형식·인프라·규정이 최종 시험과 완전 동일한 3/4 스케일 리허설 |
배점이 시간 배분을 정한다. 자동 스모크 테스트가 12점이므로 "README대로 돌아가는 것"이 최우선이고, 과정 8점은 커밋 타임라인과 세션 로그로 채점된다. 종료 직전에 몰아서 커밋 4개를 만드는 것은 타임라인에 그대로 드러나므로, 커밋은 작업이 진행되는 흐름에 맞춰 남기도록 하자.
| 구간 | 할 일 | 커밋 |
|---|---|---|
| 0~10분 | 문제 읽기, 함수 단위 분해, repo 생성 | ① 첫 커밋(빈 repo) + README 계획 |
| 10~35분 | 핵심 계산 기능(대표 입력 1개가 끝까지 돈다) | ② |
| 35~55분 | 고장 코드 디버깅(재현 → 원인 → 수정) | ③ |
| 55~75분 | 검증 3종 수행 + README 증빙 | ④ |
| 75~90분 | 클론 직후 실행 관점 최종 점검, 선언문, 업로드 | ⑤ (여유분) |
최종 시험에서는 커밋 주기와 제출 규정이 한 단계 확장되며, 전문은 부록 E에 있다. 시험2에서 커밋 리듬을 미리 몸에 붙여 두면 최종 시험의 규정에도 익숙해질 것이다.
과제: 시험 수행 절차
- (전날) 새 터미널에서 환경을 점검하라. (완료 기준:
git --version과 Claude Code 실행이 정상 응답한다) - (0~10분) 주제 공개 즉시 빈 repo를 만들고 첫 커밋을 하라. (완료 기준: GitHub 원격에 커밋 1개가 보인다)
- (0~10분) 문제를 입력→계산→출력의 함수 3~4개로 분해해 README에 계획을 적어라. (완료 기준: README에 함수 이름 목록이 있다)
- (10~35분) 핵심 계산 기능을 만들고 커밋하라. (완료 기준: 학번 파라미터의 대표 입력 1개로 실행이 끝까지 돈다)
- (35~55분) 고장 코드의 에러를 재현하고, 원인을 한 문장으로 적은 뒤 수정하고 커밋하라. (완료 기준: 에러 전문과 원인 한 줄이 README에 있다)
- (55~75분) 검증 3종을 수행하고 README에 기록한 뒤 커밋하라. (완료 기준: 단위·대조값·극한값 각 1줄 이상)
- (75~90분) repo를 새로 클론한 심정으로 README 순서대로 실행해 본 뒤 최종 커밋·업로드하라. (완료 기준: 커밋 ≥4, 선언문 1쪽 포함)
완성 기준
- 빈 repo 첫 커밋을 포함해 90분 내 커밋 4회 이상
- README대로 클론 직후 실행이 성공한다 (자동 스모크 테스트의 관점)
- README에 검증 3종 증빙이 각 1줄 이상 기록되어 있다
- AI 사용내역 선언문 1쪽이 첨부되어 있다
- 종료 시각 전에 최종 업로드가 완료되었다
막혔는가?
정답 코드가 아니라 프롬프트를 준다. 막힌 상황에 맞는 것을 골라 써라.
- 에러가 났을 때: "다음 에러 전문을 보고 원인 후보 3개와 각각의 확인 방법을 알려 줘: (에러 전문 붙여넣기)"
- 어디서 시작할지 모를 때: "이 문제를 입력·계산·출력 함수 3개로 분해하고, 함수 시그니처만 먼저 제안해 줘. 코드는 아직 쓰지 마."
- 결과가 의심될 때: "이 회귀 결과를 검증할 극한값 테스트 2개를 제안해 줘. 각 테스트가 어떤 실수를 잡는지도 설명해 줘."
시험 직후 10분: 위키 기록 긴장이 풀리기 전에 기록하라. 어디서 막혔고, 어떤 프롬프트가 살렸는지(또는 실패했는지) 1건을 위키에 커밋하라. 같은 형식의 최종 시험을 준비할 때 좋은 원자료가 된다.
요약
- S 12-1 데이터 점검 3연타:
head()→info()→describe(). 읽은 직후, 그리고 처리 전후에. - S 12-2 최소자승법은 식 (12.1)의 S(a, b) = Σ [yi − (axi + b)]² 를 최소화한다. 제곱 벌점은 이상치에 민감하다.
- S 12-3
k, b = np.polyfit(x, y, 1)의 반환 순서는 [기울기, 절편]이다. 품질은 식 (12.3)의 R²와 잔차 플롯으로 함께 본다. - S 12-4 이상치 원칙: 후보는 기계(3σ)로 찾고 판단은 조사로 한다. 수정·삭제는 반드시 README에 기록한다.
| 근원 | 데이터 속 신호 | 올바른 처리 |
|---|---|---|
| 기록·입력 실수 | 자릿수·소수점이 튄다, 원본 기록과 불일치 | 원본 대조 후 수정, 수정 내역 기록 |
| 센서·계측 고장 | 특정 시점·장비에 몰려서 튄다, 물리적으로 불가능한 값 | 해당 구간 제외 + 제외 근거 기록 |
| 진짜 물리 현상 | 재현된다, 조업 조건과 상관을 보인다 | 지우지 않는다(조사 대상으로 승격) |
도구 사다리는 이 장으로 이론 구간을 마쳤다. 단위환산(도구④)에서 시작해 증기압(⑤), T-xy(⑥), 물질수지(⑦), 압력강하(⑧)를 지나며 사이클의 ⑤ 테스트와 ⑥ 화공 검증 근육이 매주 자랐고, 시험2는 그 근육의 총연습이다. 13장부터는 만든 것을 화면에 올려 보여 주는 기술로 넘어간다.
용어 정리
- 데이터프레임(DataFrame)
- 행과 열로 이루어진 표를 담는 pandas의 자료구조. 행은 관측 한 건, 열은 변수 하나다.
- 결측값(missing value)
- 기록되지 않아 비어 있는 칸. pandas에서는 NaN으로 표시된다.
- 집계(aggregation)
- 데이터를 그룹으로 나눠 그룹별 요약 통계를 구하는 조작.
- 잔차(residual)
- 측정값에서 모델 예측값을 뺀 차이.
- 최소자승법(method of least squares)
- 잔차 제곱합을 최소화하는 모델 파라미터를 택하는 피팅 방법.
- 결정계수(coefficient of determination)
- 피팅이 데이터 산포를 설명하는 비율. R² = 1 − SSres/SStot.
- 이상치(outlier)
- 나머지 데이터의 패턴에서 크게 벗어난 점. 근원 조사 없이 지우지 않는다.
연습문제
Q군: 개념·읽기 (AI 없이 풀 수 있어야 한다)
- Q12.1 코드 12-1의 데이터에 대해
df.groupby("session")["dP_kPa"].max()의 출력을 코드 실행 없이 예측하라. - Q12.2 코드 12-6의
fit_calibration함수가 하는 일을 한국어 2~3문장으로 설명하라. 첫 줄의dropna를 지우면 어떤 문제가 생기는지도 밝혀라. - Q12.3 식 (12.1)에서 잔차를 제곱하는 이유 두 가지를 쓰고, 그중 하나가 이상치 민감성으로 이어지는 논리를 설명하라.
P군: 제작·계산 (AI 사용 전제)
- P12.1
find_outliers(df, x_col, y_col, n_sigma)함수를 만들어라. 행을 삭제하지 말고, 의심 행과 그 잔차를 담은 데이터프레임을 반환해야 한다. 코드 12-1의 데이터로 n_sigma = 3과 2의 결과 차이를 보고하라. - P12.2 이 장의 탐지 절차(describe → 그림 → 3σ)를 통과하도록 이상치를 "숨기는" 데이터 조작 방법을 두 가지 제시하라. 각 방법이 검증 루틴 3종 중 무엇에는 여전히 걸리는지 밝혀라.
- P12.3 최종 시험 주제 예시 "학번별 실험 데이터 회귀 + 이상치 자동 탐지 리포트 생성기"와 동형의 도구를 만들어라: CSV를 받아 회귀 계수·R²·이상치 후보 목록·검증 3종 결과를 담은 마크다운 리포트를 생성한다. 빈 repo 첫 커밋부터 시작해 90분 타이머로 수행하고 커밋 4회 이상을 남겨라.
위키 기록 과제
이번 주 의미 커밋 1회 이상이 채점 지표다. 다음 3항을 기록하라.
- 이번 장 핵심 1페이지 정리(#seed): 점검 3연타와 최소자승·이상치 절차를 본인의 말로.
- "내가 틀렸던 것" 1건: 시험2에서 만난 에러 전문 스크린샷과 원인 한 줄.
- 타 과목 연결 1건: 실험 과목 보고서의 데이터 처리에 이 장의 절차를 적용해 본 기록.
13장 시각화·대시보드·기계학습 맛보기
이번 주의 도구: ⑨ 공정 데이터 대시보드
다음 상황을 생각해 보자. 어느 공장의 신입 엔지니어가 매일 아침 전날의 조업 데이터 CSV를 받아 그래프 여덟 장을 만들어 회의에 올린다. 스크립트를 실행하고, 그림을 저장하고, 보고 자료에 붙여 넣는 일을 매일 반복한다. 회의 중 팀장이 "지난주 같은 요일과 겹쳐 보여 줄 수 있나?"라고 물으면, 자리로 돌아가 코드를 고치고 다시 그려 오는 데 반나절이 걸린다.
더 곤란한 순간도 있다. 화면에 띄운 그래프를 보고 누군가 "이 세로축 단위가 뭔가요?"라고 묻는데 축에는 숫자만 있고 단위가 없다. 그래프를 만든 본인도 즉답하지 못한다. 이 장은 이 두 가지 문제를 다룬다. 그래프를 검증 가능한 주장으로 만드는 문법, 그리고 질문이 나오는 순간 그 자리에서 조건을 바꿔 보여 주는 대시보드다. 끝에서는 12장의 회귀가 신경망이라는 더 큰 세계로 어떻게 이어지는지 맛본다.
이 장에서 다루는 것
- 공학 그래프의 필수 4요소를 점검하는 눈을 익힌다
- 증기압 곡선으로 로그축이 필요한 상황을 확인한다
- matplotlib 스크립트를 Streamlit 대시보드로 바꾼다
- 훈련/시험 분리로 과적합을 잡아낸다
- DFT와 기계학습 포텐셜이라는 연구 현장의 기계학습을 엿본다
학습목표 이 장을 마치면 다음을 할 수 있다.
- 그래프에서 축 라벨·단위·범례·축 스케일의 결함을 찾아 교정할 수 있다.
- 지수적으로 변하는 데이터에 로그축을 써야 하는 이유를 설명할 수 있다.
- matplotlib 스크립트를 위젯이 있는 Streamlit 대시보드로 전환할 수 있다.
- 훈련 오차와 시험 오차를 구분하고 과적합을 판별할 수 있다.
- 회귀와 신경망을 모델–손실–학습의 공통 구조로 설명할 수 있다.
- 도구⑨와 서브프로젝트 v1을 완성 기준에 따라 점검할 수 있다.
이번 주 실습에서는 §13.1~13.2를 사용해 도구⑨ 공정 데이터 대시보드를 만들고, 남은 시간은 서브프로젝트 스프린트(순회 컨설팅 + 페어 상호 코드리뷰)에 쓴다. 최종 시험 주제 예시 중 하나가 "학번별 실험 데이터 회귀 + 이상치 자동 탐지 리포트 생성기"다. 12장과 이 장이 그 직접 대비 범위다.
13.1 공학 그래프의 문법
과학 계산의 목적은 숫자가 아니라 통찰이다. 계산이 쏟아 낸 수백 개의 숫자를 사람이 이해하려면 그림이 필요하고, 그래서 시각화는 해석의 시작 단계다. 그리고 그래프는 "이 온도에서 압력이 이만큼이다"라는 주장이고, 주장에는 근거가 붙어 있어야 한다.
공학 그래프가 갖춰야 할 최소 요건은 네 가지다. 첫째, 두 축에 물리량 이름과 단위를 적는다. 단위 없는 축은 검증 루틴 중 단위 체크를 원천적으로 불가능하게 만든다. 둘째, 곡선이 둘 이상이면 어느 선이 무엇인지 밝힌다. 여러 데이터 계열을 구분하는 이름표를 범례(legend)라 한다. 셋째, 데이터의 성격에 맞는 표현을 고른다. 측정한 데이터는 점(마커)으로, 모델이나 상관식은 선으로 그린다. 측정점 열두 개를 실선으로만 이어 버리면 측정하지 않은 중간값까지 측정한 것처럼 주장하는 셈이 된다. 넷째, 데이터의 분포에 맞는 축 스케일을 고른다.
넷째 요건이 낯설 수 있으니 8장의 증기압으로 확인해 보자. 물의 증기압은 NIST 상수의 유효범위인 379~573 K 사이에서 약 1 bar에서 약 70 bar까지, 70배 가까이 변한다. 이것을 눈금이 일정한 보통 축(선형축)에 그리면 고온부의 큰 값이 축 전체를 차지해 저온부 곡선은 바닥에 붙은 0처럼 보인다. 눈금 한 칸이 일정한 배수(10배)를 나타내는 축을 로그축(logarithmic axis)이라 한다. 로그축에서는 1 bar와 70 bar가 같은 비중으로 보이고, 저온부의 변화도 읽을 수 있다.
증기압에 로그축이 어울리는 것은 우연이 아니다. Antoine 식 자체가 log10 p*를 온도의 함수로 쓰는 식이므로, 세로축을 로그로 잡으면 곡선이 완만하게 펴진다. 데이터를 만든 물리가 지수적이면 축도 로그로 잡는다. 이것이 스케일 선택의 원칙이다.
예제 13.1-1 AI가 그린 증기압 곡선 채점하기
문제 물의 Antoine 상수(NIST WebBook, log10(p*/bar) = A − B/(T + C), A = 3.55959, B = 643.748, C = −198.043, 유효범위 379~573 K)를 사용해 증기압 곡선을 그리는 코드를 AI로 생성하고, 생성된 그래프를 4요소 기준으로 채점·교정하라.
풀이 전략 작도 코드는 AI에게 맡기고, 그래프 채점과 온도 범위의 결정은 직접 한다. 먼저 일부러 정보를 덜 준 프롬프트로 시작해, 생성된 그래프의 결함을 찾는 훈련을 겸한다(AI 협업 사이클 ③~④).
물의 증기압 곡선을 matplotlib으로 그려 줘. Antoine 식 써서. 상수는 A=3.55959, B=643.748, C=-198.043이고 압력은 bar야.
import numpy as np
import matplotlib.pyplot as plt
A, B, C = 3.55959, 643.748, -198.043
T = np.linspace(300, 600, 100)
p = 10**(A - B/(T + C))
plt.plot(T, p)
plt.show()
✔ 실행은 되고 곡선도 나온다. 그러나 4요소 채점 결과: 축 라벨과 단위 없음, 범례 없음, 선형축이라 400 K 아래 구간이 바닥에 붙어 보이지 않음. 결정적으로 온도 범위 300~600 K는 AI가 임의로 고른 것인데, 상수의 유효범위(379~573 K)를 양쪽으로 벗어난 외삽이다. 8장에서 373 K에서조차 25%나 낮은 값이 나오는 것을 보았다. 그보다 더 먼 외삽 구간을 곡선에 포함하면 안 된다.
결함 네 가지를 그대로 나열해 교정을 요구하면 다음을 얻는다.
고칠 것: ① 온도 범위를 유효범위 379~573 K로 제한 ② 축 라벨에 물리량과 단위 표기 ③ 범례 추가 ④ 세로축을 로그축으로. 주석은 한국어로, 물리량 변수에는 단위 주석을 달아 줘.
import numpy as np
import matplotlib.pyplot as plt
# NIST WebBook: 물, 유효범위 379~573 K
# log10(p*/bar) = A - B/(T + C)
A, B, C = 3.55959, 643.748, -198.043
T = np.linspace(379, 573, 100) # 온도 [K] — 유효범위만
p = 10**(A - B/(T + C)) # 증기압 [bar]
plt.semilogy(T, p, label="물 (NIST 상수)") # 세로축 로그
plt.xlabel("온도 T [K]")
plt.ylabel("증기압 p* [bar]")
plt.legend()
plt.grid(True)
plt.savefig("vapor_curve.png", dpi=150)
✔ 4요소 충족 확인. 곡선은 로그축에서 거의 직선으로 펴진다. Antoine 식이 log10 p*의 식이므로 예상과 일치한다.
- 단위 체크: 가로축 T [K], 세로축 p* [bar]로 식의 입출력 단위와 일치
- 대조값: 곡선의 저온 끝(379 K = 약 106 °C)에서 값이 약 1.0 bar로, "물은 1 atm(= 101.325 kPa) 부근에서 100 °C에 끓는다"는 상식과 어긋나지 않는다
- 극한값 sanity: 온도가 오를수록 증기압이 단조 증가하고, 573 K에서 약 70 bar다. 감소 구간이 있다면 코드나 상수를 의심해야 한다
분석 그래프의 결함은 코드의 결함보다 발견하기 어렵다. 문법 오류는 실행이 멈춰서 저절로 드러나지만, 라벨 없는 그래프와 외삽된 곡선은 멀쩡한 그림으로 출력되기 때문이다. 그래서 채점 기준(4요소)을 명시적으로 들고 있어야 잡는다. 특히 "AI가 임의로 고른 축 범위"는 이 장 이후 모든 그래프에서 반드시 확인할 항목이다. 학번별 물질의 상수로 같은 곡선을 재생성하고, 로그축을 껐을 때 저온부가 어떻게 보이는지 비교해 보라.
스스로 점검
- 축 라벨에 단위가 없으면 검증 루틴 3종 중 어느 것부터 불가능해지는가?
- 측정 데이터 12점을 마커 없이 실선으로만 이어 그렸다. 이 그래프가 하는 부당한 주장을 한 문장으로 말하라.
13.2 스크립트에서 대시보드로: Streamlit 최소한
13.2.1 일단 띄워 보기
먼저 손을 움직여 보자. 터미널에서 pip install streamlit으로 라이브러리를 설치한 뒤, 다음 다섯 줄을 app.py로 저장한다.
import streamlit as st
st.title("나의 첫 대시보드") # 페이지 제목
T = st.slider("온도 [K]", 379, 573, 400) # 슬라이더 (최소, 최대, 초기값)
st.write(f"선택한 온도: {T} K") # 화면에 출력
실행은 python app.py가 아니라 streamlit run app.py다. 브라우저가 열리고, 슬라이더가 있는 웹 페이지가 나타난다. 슬라이더를 끌면 아래 숫자가 즉시 바뀐다. (아무것도 뜨지 않으면 터미널에 출력된 주소를 브라우저에 직접 입력해 보라.)
방금 만든 것에 이름을 붙이자. 데이터와 계산 결과를 한 화면에 모아 사용자가 직접 조작하며 보게 만든 것을 대시보드(dashboard)라 하고, 슬라이더·선택 상자처럼 사용자 입력을 받는 화면 요소를 위젯(widget)이라 한다. Streamlit은 파이썬 스크립트를 이런 웹 대시보드로 바꿔 주는 라이브러리다.
동작 원리는 하나만 알면 된다. 위젯 값이 바뀔 때마다 Streamlit은 스크립트 전체를 위에서 아래로 다시 실행한다. 새로운 프로그래밍 모델을 배우는 것이 아니라, 지금까지 쓰던 "위에서 아래로 흐르는 스크립트"가 위젯 값만 갱신된 채 반복 실행되는 것이다. 그래서 5장부터 만들어 온 함수들을 고치지 않고 그대로 가져다 쓸 수 있다.
13.2.2 화공 도구의 대시보드화
이제 8장의 증기압 계산기를 대시보드로 옮겨 보자. 핵심은 두 가지다. 계산 함수는 새로 짜지 않고 재사용하며, 위젯의 범위를 상수의 유효범위로 제한한다.
예제 13.2-1 증기압 대시보드
문제 예제 13.1-1의 Antoine 상수를 사용해, 슬라이더로 온도를 고르면 증기압과 곡선 전체를 보여 주는 대시보드를 만들어라. 슬라이더는 유효범위 밖을 선택할 수 없어야 한다.
풀이 전략 전환 작업은 AI에게 맡기되, 프롬프트에 재사용할 함수, 위젯 범위, 표시할 항목 세 가지를 못 박는다. 유효범위 제한을 코드가 아니라 위젯 자체에 심는 것이 이 예제의 요점이다(사이클 ③~⑥).
이 Antoine 증기압 계산 코드를 Streamlit 대시보드로 바꿔 줘. 요구사항: ① 온도는 st.slider로 받되 범위를 유효범위 379~573 K로 제한 ② 선택 온도의 증기압을 크게 표시 ③ 그 아래 유효범위 전체의 증기압 곡선 ④ 주석은 한국어, 물리량 변수에 단위 주석.
import numpy as np
import streamlit as st
# NIST WebBook: 물(379~573 K), log10(p*/bar) = A - B/(T + C)
A, B, C = 3.55959, 643.748, -198.043
st.title("증기압 대시보드 — 물")
T = st.slider("온도 [K]", 379, 573, 400) # 유효범위로 제한 [K]
p = 10**(A - B/(T + C)) # 증기압 [bar]
st.metric("증기압", f"{p:.2f} bar") # 숫자 카드 표시
T_arr = np.linspace(379, 573, 100) # 곡선용 온도 [K]
p_arr = 10**(A - B/(T_arr + C)) # 증기압 [bar]
st.line_chart({"p* [bar]": p_arr})
✔ 슬라이더 범위가 379~573으로 제한됨을 확인. 다만 st.line_chart의 가로축이 온도가 아니라 배열 순번(0~99)으로 표시된다. 4요소 위반이므로 "가로축을 온도 값으로 표시하라"고 후속 요청해 pandas로 인덱스를 온도로 지정하게 했다. 이런 후속 교정까지가 전환 작업이다.
- 단위 체크: 슬라이더 입력 K, 표시 출력 bar로, 함수의 입출력 단위와 일치
- 대조값: 슬라이더를 최저 379 K로 내리면 1.00 bar로, 끓는점 상식(100 °C 부근, 1 atm)과 정합
- 극한값 sanity: 슬라이더가 유효범위 밖으로 아예 내려가지 않는다. 외삽 실수를 인터페이스가 차단
분석 대시보드의 이득은 편리함보다 검증을 인터페이스에 심을 수 있다는 데 있다. 스크립트에서는 사용자가 유효범위 밖 온도를 넣는 실수를 코드 안의 검사문으로 막아야 했지만, 대시보드에서는 그런 입력이 아예 불가능하도록 위젯을 설계할 수 있다. What-if: 슬라이더 범위를 300~600 K로 풀어 보라. 어떤 위험이 되살아나며, 사용자는 그 위험을 화면 어디에서도 알 수 없다는 점을 확인하라.
생각해보기 도구⑤~⑧ 중 하나를 골라 대시보드로 만든다고 하자. 어떤 값을 위젯으로 열고, 어떤 값을 코드에 못 박아 두겠는가? "사용자가 바꿔도 안전한가"를 기준으로 나누고 근거를 말하라.
13.3 회귀에서 신경망으로: 모델·손실·학습
13.3.1 두 얼굴의 오차
12장에서 데이터에 직선과 다항식을 맞추는 회귀(regression)를 다뤘다. 그 작업을 세 부분으로 나눠 이름을 붙이면 기계학습 전체의 뼈대가 된다. 모양을 가진 함수(모델), 모델이 얼마나 틀렸는지 재는 함수, 그리고 틀림이 줄어들도록 모델의 매개변수(parameter)를 조정하는 절차(학습)다. 틀림을 재는 함수를 손실 함수(loss function)라 한다.
가장 기본적인 손실 지표는 평균절대오차(mean absolute error, MAE)다. 데이터 n개에 대해 다음과 같이 정의한다.
MAE = (1/n) Σ |yi − ŷi| (13.1)
여기서 yi는 실제 값, ŷi는 모델의 예측값이다. MAE는 오차의 평균 크기를 그대로 알려 주고, 제곱을 쓰는 RMSE는 큰 오차를 더 무겁게 벌준다. 모델 성적을 눈으로 보려면 가로축에 실제 값, 세로축에 예측값을 찍는다. 이런 산점도를 패리티 플롯(parity plot)이라 하며, 완벽한 모델이라면 모든 점이 대각선 위에 놓인다.
성적의 기준선도 필요하다. 모든 입력에 대해 평균 하나만 답하는 상수 모델은 뜻밖에 유용한 기준선이다. 이보다 못한 모델은 아무것도 배우지 못한 것이다. 그런데 여기에 함정이 하나 있다. 지금까지처럼 갖고 있는 데이터 전부로 피팅하고 같은 데이터로 성적을 매기면, 그 성적은 무엇을 말해 주는가? 모델을 만드는 이유는 아직 보지 못한 데이터를 예측하기 위해서다. 그래서 데이터를 둘로 가른다. 모델을 맞추는 데 쓰는 부분을 훈련 데이터(training data), 성적을 매기기 위해 감춰 두는 부분을 시험 데이터(test data)라 한다. 학생이 시험 문제를 미리 보고 공부하면 점수가 실력을 말해 주지 않는 것과 같은 이치다.
예제 13.3-1 훈련 오차와 시험 오차는 다르게 움직인다
문제 반응 온도 60~100 °C에서 수율(%)을 측정한 가상 조업 데이터 16점을 난수로 생성하고, 훈련 12점/시험 4점으로 나눠 1차·2차·9차 다항식을 피팅하라. 차수별로 훈련 MAE와 시험 MAE를 비교하라.
풀이 전략 참 관계를 우리가 직접 심은 가상 데이터를 쓴다. 그래야 어떤 모델이 "맞는" 모델인지 답을 알고 채점할 수 있다. 코드는 12장에서 쓴 polyfit 그대로라 AI 없이 직접 짠다.
import numpy as np
rng = np.random.default_rng(1) # 재현 가능한 난수 시드
T = np.linspace(60, 100, 16) # 반응 온도 [degC]
y = 20 + 1.5*T - 0.008*T**2 # 우리가 심은 참 관계 (2차) [%]
y = y + rng.normal(0, 1.0, T.size) # 측정 잡음 [%p]
idx = rng.permutation(T.size) # 순서를 무작위로 섞는다
train, test = idx[:12], idx[12:] # 훈련 12점 / 시험 4점
for deg in [1, 2, 9]:
coef = np.polyfit(T[train], y[train], deg) # 훈련 데이터로만 피팅
err_tr = np.polyval(coef, T[train]) - y[train] # 훈련 오차 [%p]
err_te = np.polyval(coef, T[test]) - y[test] # 시험 오차 [%p]
print(f"{deg}차: 훈련 MAE {np.mean(np.abs(err_tr)):.2f}, "
f"시험 MAE {np.mean(np.abs(err_te)):.2f}")
실행 결과는 다음과 같다.
1차: 훈련 MAE 1.19, 시험 MAE 0.87
2차: 훈련 MAE 0.52, 시험 MAE 0.45
9차: 훈련 MAE 0.23, 시험 MAE 2.71
훈련 MAE만 보면 9차가 최고다. 차수를 올릴수록 훈련 오차는 반드시 줄어든다. 유연한 곡선일수록 점들을 더 바짝 통과할 수 있기 때문이다. 그러나 시험 MAE는 정반대의 이야기를 한다. 9차의 시험 오차는 2차의 여섯 배다. 9차 곡선은 측정 잡음까지 통째로 외워 버렸고, 그 암기는 새 데이터 앞에서 무너진다.
- 단위 체크: 수율과 오차 모두 %p이므로 온도(°C)와 섞이지 않았는지 확인
- 대조값: 2차 피팅의 계수가 우리가 심은 참 관계(20, 1.5, −0.008)에 가까운지 출력해 비교하라(가상 데이터의 특권이다)
- 극한값 sanity: 9차 곡선을 범위 밖 55 °C로 외삽해 보라. 수율이 물리적으로 불가능한 값으로 발산한다
분석 훈련 데이터의 잡음까지 외워 새 데이터에서 오히려 나빠지는 현상을 과적합(overfitting)이라 한다. 유연한 모델일수록 같은 문제라도 훈련 데이터가 조금만 달라지면 전혀 다른 곡선을 내놓는다. 그 민감함이 과적합의 정체다. 그리고 2차가 이긴 이유를 눈여겨보라. 데이터를 만든 참 관계가 2차였다. 데이터의 물리를 아는 사람이 모델의 모양을 잘 고른다. 화공 엔지니어가 기계학습에서 갖는 이점이 여기에 있다. What-if: 시드를 학번 뒷자리로 바꿔 세 차수의 순위가 유지되는지 확인해 보라.
실무 데이터에는 참 관계가 없으므로 결정계수(coefficient of determination, R²) 같은 지표로 판단을 보조한다. 화학공학의 실측 데이터에서는 R²가 0.9를 넘으면 대체로 훌륭한 수준이고, 0.95를 넘으면 오히려 과적합을 의심하라는 경험칙이 있다. 높은 점수가 항상 좋은 소식은 아니다.
13.3.2 신경망: 모양을 데이터가 정하는 함수
직선 모델의 매개변수는 기울기와 절편 2개다. 9차 다항식은 10개다. 이 수를 더 늘리면 무슨 일이 생기는가? 신경망(neural network)은 "입력의 가중합을 구하고 단순한 비선형 변환을 거치는" 작은 함수를 층층이 쌓아 만든, 매개변수가 대단히 많은 유연한 함수다. 다항식에서는 사람이 차수라는 모양을 골랐지만, 신경망은 모양 자체를 데이터가 정하게 한다.
그렇다고 새 수학이 등장하는 것은 아니다. 신경망의 학습도 손실 함수가 작아지는 방향으로 매개변수를 조금씩 반복 조정하는 절차이고, 성적은 시험 데이터의 MAE나 패리티 플롯으로 매긴다. 12장의 회귀에서 배운 모델–손실–학습의 3요소가 규모만 커진 채 그대로 있다. 1장에서 만난 대형 언어 모델도 다음 토큰을 예측하도록 이 절차로 훈련된 매우 큰 신경망이다. 학기 내내 함께 일한 AI가 사실은 이 절의 주제였던 셈이다.
유연함에는 대가가 따른다. 매개변수가 많을수록 과적합의 위험이 커지고, 필요한 데이터가 많아지고, 왜 그런 답이 나왔는지 해석하기 어려워진다. 그래서 신경망에서는 훈련/시험 분리를 반드시 지켜야 한다. 예제 13.3-1에서 9차 다항식이 보여 준 붕괴는, 매개변수가 수백만 개인 모델에서는 더 조용하고 더 그럴듯한 모습으로 일어난다.
생각해보기 어떤 모델의 시험 MAE가 상수 모델(평균만 답하는 기준선)보다 나쁘게 나왔다. 모델을 더 키우기 전에 의심해야 할 것은 무엇인가? 힌트: 입력 열과 정답 열의 순서가 서로 어긋나 짝이 깨진 데이터는 어떤 모델로도 배울 수 없다.
① 문제 정의 → ② 분해 → ③ 프롬프트 → ④ 생성 코드 읽기 → ⑤ 테스트 → ⑥ 화공 검증
대시보드는 ⑤·⑥을 화면 위로 끌어올린다. 위젯 범위가 곧 유효범위 검증이고, 실시간 그래프가 sanity 체크다. 기계학습에서는 ⑥이 "시험 데이터로만 채점한다"는 규칙이 된다.
13.4 칼럼: 화공 연구 현장의 기계학습, DFT와 MLIP
이 장에서 배운 3요소가 연구 현장에서 어떻게 쓰이는지 잠깐 엿보자. 새 촉매를 찾는 계산화학 연구에서는 반응물 분자가 촉매 표면에 얼마나 강하게 붙는지(흡착 에너지)가 성능을 가르는 핵심 물성이다. 이 값은 양자역학으로 분자와 재료의 에너지를 계산하는 밀도범함수이론(density functional theory, DFT)으로 구하는데, 계산 한 건에 많은 시간이 든다. 그래서 연구자들은 이미 수행된 대량의 DFT 결과를 훈련 데이터 삼아, 새 촉매 후보의 흡착 에너지를 곧바로 예측하는 기계학습 모델을 만든다. 이 분야에서는 실용적 의미가 있으려면 흡착 에너지의 MAE가 0.1~0.2 eV 수준이어야 한다는 목표가 통용된다. 모델의 성패를 가르는 잣대가 결국 시험 데이터의 MAE라는 점이 이 장의 예제와 같다.
한 걸음 더 나아간 도구도 있다. 원자들의 배치를 입력받아 에너지와 힘을 출력하도록 훈련된 신경망을 기계학습 포텐셜(machine-learned interatomic potential, MLIP)이라 한다. DFT의 정확도에 가까운 답을 훨씬 적은 비용으로 내는 것이 목표이며, 전지 소재나 촉매처럼 원자 수가 많은 시스템의 시뮬레이션에 쓰인다. 여기서도 뼈대는 모델–손실–학습 그대로이고, "훈련 범위 밖의 원자 배치에서는 믿을 수 없다"는 외삽 경고까지 Antoine 상수의 유효범위와 같은 논리로 따라온다. 13주차 이론 세션에서 담당 교수의 실제 연구 사례로 이 두 주제를 소개한다.
13.5 실습 13: 도구⑨ 공정 데이터 대시보드 + 서브프로젝트 스프린트
이번 실습(2시간)의 권장 배분은 도구⑨ 제작 45분, 서브프로젝트 스프린트 60분, 퇴실 전 위키 기록 15분이다. 도구⑨는 이번 학기 도구 사다리의 마지막 칸이다.
준비물
pip install streamlit설치 확인 (터미널에서streamlit hello가 브라우저를 열면 성공)- 스타터 repo의 공정 조업 데이터 CSV (시간·온도·압력·유량·수율 열)
- 재사용할 이전 도구: 12주차 이론에서 다룬 pandas 로딩·피팅 코드, 그리고 도구⑤~⑧ 중 계산 함수 1개 이상을 import한다
과제
- 실습 13.1 CSV를 pandas로 읽어 화면에 표로 표시하는 대시보드 뼈대를 만들어라. (성공 시
streamlit run dashboard.py로 브라우저에 데이터 표가 뜬다) 여기서 1차 커밋하라. - 실습 13.2 열 하나를 골라 시간에 대한 그래프를 추가하고, 4요소(축 라벨+단위·범례·마커/선·스케일)를 점검하라. (성공 기준: §13.1의 4요소 채점을 스스로 통과)
- 실습 13.3 위젯 2개를 달아라: 표시할 변수를 고르는 선택 상자 1개, 표시 구간을 자르는 슬라이더 1개. (성공 시 위젯 조작에 따라 그래프가 실제로 바뀐다) 여기서 2차 커밋하라.
- 실습 13.4 이전 도구의 계산 함수 1개를 연결하라. 예: 온도 열에 도구⑤의 증기압 함수를 적용해 파생 열을 만들고 함께 그린다. 위젯 범위는 그 함수의 유효범위로 제한하라. (성공 기준: 유효범위 밖 입력이 화면에서 불가능)
- 실습 13.5 README에 검증 3종(단위·대조값·극한값) 수행 기록과 대시보드 스크린샷 1장을 넣고 최종 커밋하라.
완성 기준 (통과/재도전 2단계. 재도전은 다음 실습 전까지)
streamlit run dashboard.py가 오류 없이 페이지를 띄운다- 위젯 2개 이상이 그래프를 실제로 바꾼다
- 모든 그래프가 4요소 점검을 통과한다
- 이전 도구의 함수 1개 이상을 import해 재사용했다
- README에 검증 3종 기록 + 스크린샷 1장
- 커밋 3회 이상 (기능 단위 메시지)
막혔는가?
- "
streamlit run을 실행했는데 브라우저에 아무것도 안 뜬다. 터미널 출력 전문을 붙여넣을 테니 원인 후보 3개와 각각의 확인 방법을 알려 줘." - "이 matplotlib 코드를 Streamlit 대시보드로 바꿔 줘. 위젯은 변수 선택 상자 하나만. 코드 전체를 다시 쓰지 말고 바뀐 부분과 그 이유만 설명해 줘."
- "CSV 열 이름 때문에
KeyError가 난다. 에러 메시지 전문과df.columns출력을 붙여넣을 테니 어느 쪽을 고쳐야 하는지 알려 줘."
서브프로젝트 스프린트 가이드 (60분)
9주차에 선언한 서브프로젝트의 v1을 오늘 만든다. v1은 완성이 아니라 핵심 기능 하나가 입력부터 출력까지 끊기지 않고 작동하는 상태를 말한다. 다음 네 가지를 점검하라.
- 핵심 기능 1개가 end-to-end로 작동하는가. 화면이 아니라 실행으로 보여라.
- 본인의 사다리 산출물 2개 이상을 재사용했는가 (서브프로젝트 공통 요건). import 문이 그 증거다.
- 검증 루틴 중 1종 이상이 내장돼 있는가.
- README 골격(문제정의·실행법·검증)이 잡혀 있는가.
스프린트 중 순회 컨설팅에서 물어볼 질문 1개를 미리 준비하라. "코드를 봐 달라"가 아니라 "이 설계 결정이 맞는지"를 물어야 60분이 산다. 페어 상호 코드리뷰는 다음 순서로 한다. 상대의 3분 EiPE 설명을 듣고, "이 줄을 바꾸면 어떻게 되는가" 질문을 하나 던지고, 검증 증빙을 확인한다. 이 형식은 14주 발표와 최종 시험 구술 방어의 리허설에 해당한다.
퇴실 전 기록 오늘 만든 것·막힌 것·틀린 것 중 1건을 위키에 커밋해야 퇴실한다. 추천 소재: 위젯이 그래프를 바꾸지 않던 순간의 원인, 또는 훈련 MAE와 시험 MAE가 갈라지던 실행 결과.
요약
S 13-1 공학 그래프 4요소: ① 축 라벨+단위 ② 범례 ③ 측정은 마커, 모델은 선 ④ 데이터에 맞는 축 스케일(지수적 변화는 로그축). AI가 그린 그래프는 항상 이 기준으로 채점한다(축 범위가 유효범위 안인지 포함해서).
S 13-2 MAE = (1/n) Σ |yi − ŷi|, 식 (13.1). 성적은 반드시 시험 데이터로 매긴다. 훈련 오차는 모델을 키우면 항상 줄지만 시험 오차는 그렇지 않다. 갈라지는 지점이 과적합의 시작이다.
S 13-3 대시보드 전환 프롬프트 패턴: "기존 함수를 import해 재사용하고, 위젯 범위는 유효범위로 제한하고, 주석은 한국어·단위 명기." 검증을 인터페이스에 심는 것이 대시보드의 본질이다.
| 항목 | 회귀 (12장) | 신경망 |
|---|---|---|
| 모델의 모양 | 사람이 고른다 (직선·다항식·Antoine) | 데이터가 정한다 (유연한 층 구조) |
| 매개변수 수 | 몇 개~십수 개 | 대단히 많다 |
| 필요한 데이터 | 적어도 된다 | 많이 필요하다 |
| 결과 해석 | 계수의 물리적 의미를 읽을 수 있다 | 직접 해석이 어렵다 |
| 공통점 | 모델–손실–학습의 3요소, 시험 데이터로 채점, 훈련 범위 밖 외삽 금지 | |
도구 사다리가 완성됐다. 단위환산에서 시작해 물성 조회, 증기압, T-xy, 물질수지, 압력강하, 데이터 피팅을 지나 대시보드까지, 아홉 개의 도구가 서로를 import하는 하나의 도구상자가 됐다. AI 협업 사이클로 보면 이 장은 ⑤ 테스트와 ⑥ 화공 검증을 화면 위로 끌어올린 주였다. 이제 만들기는 끝났고, 남은 것은 보여 주기다. 14장에서 이 도구상자를 3분 안에 설명하는 법을 다룬다.
용어 정리
- 범례(legend)
- 그래프에서 여러 데이터 계열을 구분해 주는 이름표.
- 로그축(logarithmic axis)
- 눈금 한 칸이 일정한 배수를 나타내는 축. 수십 배 이상 변하는 데이터에 쓴다.
- 대시보드(dashboard)
- 데이터와 계산 결과를 한 화면에 모아 사용자가 조작하며 보게 만든 것.
- 위젯(widget)
- 슬라이더·선택 상자처럼 사용자 입력을 받는 화면 요소.
- 회귀(regression)
- 데이터에 함수를 맞춰 변수 사이의 관계를 추정하는 방법.
- 매개변수(parameter)
- 모델의 모양을 결정하는 조정 가능한 수. 직선 모델이라면 기울기와 절편.
- 손실 함수(loss function)
- 모델의 예측이 실제와 얼마나 다른지를 하나의 수로 재는 함수.
- 평균절대오차(mean absolute error, MAE)
- 예측 오차의 절대값 평균. 식 (13.1).
- 패리티 플롯(parity plot)
- 실제 값 대 예측값의 산점도. 완벽한 모델이면 점이 모두 대각선 위에 놓인다.
- 훈련 데이터(training data)
- 모델의 매개변수를 맞추는 데 쓰는 데이터.
- 시험 데이터(test data)
- 모델 성적을 매기기 위해 피팅에서 감춰 두는 데이터.
- 과적합(overfitting)
- 훈련 데이터의 잡음까지 외워 새 데이터에서 성능이 오히려 나빠지는 현상.
- 결정계수(coefficient of determination, R²)
- 데이터 변동 중 모델이 설명하는 비율. 1에 가까울수록 설명력이 높다.
- 신경망(neural network)
- 가중합과 비선형 변환으로 이뤄진 작은 함수를 층층이 쌓은, 매개변수가 많은 유연한 모델.
- 밀도범함수이론(density functional theory, DFT)
- 양자역학에 기반해 분자·재료의 에너지를 계산하는 방법.
- 기계학습 포텐셜(machine-learned interatomic potential, MLIP)
- 원자 배치로부터 에너지와 힘을 예측하도록 훈련된 신경망.
연습문제
Q군: 개념·읽기 (AI 없이 풀 것)
- Q13.1 어떤 그래프가 세로축에 "P"라고만 적혀 있고, 측정점 10개가 실선으로만 이어져 있으며, 곡선 두 개에 범례가 없다. 4요소 기준으로 결함 세 가지를 지적하고 각각의 교정 방법을 쓰라.
- Q13.2 코드 13-3의 목적과 동작을 한국어 2~3문장으로 설명하라. 슬라이더를 움직이면 이 코드의 어느 부분이 다시 실행되는지도 말하라.
- Q13.3 예제 13.3-1처럼 극단적인 9차 다항식을 쓰지 않고도 과적합을 일부러 만드는 방법을 두 가지 제시하라. 각 방법이 검증 루틴 또는 훈련/시험 비교 중 무엇에 걸리는지 설명하라.
P군: 제작·계산 (AI 사용 전제)
- P13.1 도구⑨에 이동평균 창 크기를 고르는 슬라이더를 추가하고, 원본과 이동평균 곡선을 범례로 구분해 함께 그려라. 창 크기를 극단(1, 데이터 길이)으로 밀었을 때의 결과를 README에 기록하라.
- P13.2 학번별 물질의 Antoine 상수로 예제 13.2-1의 대시보드를 다시 만들어라. 슬라이더 범위를 그 물질의 유효범위로 제한하고, 검증 3종 수행 기록(상수 출처 포함)을 README에 남겨라.
- P13.3 과적합 탐험기를 만들어라: 코드 13-5의 데이터에 대해 다항식 차수를 슬라이더(1~12)로 고르면 피팅 곡선과 훈련/시험 MAE 두 값이 실시간 갱신되는 대시보드. 훈련 MAE와 시험 MAE가 갈라지기 시작하는 차수를 찾아 README에 보고하라.
위키 기록 과제 (이번 주 의미 커밋 1회 이상이 채점 지표다)
- 이 장 핵심 1페이지 정리(#seed): 그래프 4요소 + 모델–손실–학습 3요소 + "성적은 시험 데이터로"를 자기 언어로.
- "내가 틀렸던 것" 1건: 위젯·KeyError·과적합 등 오늘의 실수를 에러 화면 스크린샷과 함께.
- 타 과목 연결 1건: 화공양론이나 유체역학 실험 데이터 중 대시보드에 올리고 싶은 것 하나와 그 이유.
14장 엔지니어의 발표법
이번 주의 산출물: 서브프로젝트 발표(3분 데모영상 + 라이브 Q&A 2분)
다음 상황을 생각해 보자. 입사 첫 달의 신입 엔지니어가 공정 데이터 대시보드를 만들어 팀 회의에서 시연한다. 화면은 매끄럽게 넘어가고 그래프도 그럴듯하다. 3분이 지나자 팀장이 묻는다. "이상치를 걸러냈다고 했는데, 그 기준값은 어디서 나온 겁니까? 그리고 이 줄, 여기서 뭘 하는 겁니까?"
코드는 AI가 짰고, 돌아가는 것은 여러 번 확인했다. 그러나 그 두 질문에는 답하지 못했다. 회의실에서 무너진 것은 도구가 아니라 설명이었다. 이 장은 그 3분과 그 뒤의 2분을 준비하는 방법을 다룬다.
이 장에서 다루는 것
- 기술 발표를 문제정의→데모→화공 해석의 3분 구성으로 설계한다
- 청중 질문을 네 유형으로 분류하고 유형별 방어 전략을 세운다
- "설명 불능 = 0점" 규정이 발표장에서 어떻게 집행되는지 확인한다
- README·검증 증빙·재사용 도구를 점검하는 프로젝트 완성 체크리스트를 만든다
- 최종 시험 운영 규정과 비상 프로토콜을 점검한다
학습목표: 이 장을 마치면 다음을 할 수 있다.
- 3분 발표를 문제정의·데모·화공 해석의 세 구간으로 나누고 시간 예산을 배분할 수 있다.
- 청중 질문을 네 유형으로 분류하고, 각 유형이 최종 시험 루브릭의 어느 배점과 연결되는지 설명할 수 있다.
- 지목된 코드 줄을 EiPE 다섯 요소(목적·입력·처리·출력·한계)의 형식으로 2~3문장에 설명할 수 있다.
- README에 실행법·검증 증빙·재사용 도구를 명시했는지 스크립트로 점검할 수 있다.
- 데모 실패에 대비한 백업 계획을 발표 구성에 내장할 수 있다.
이번 주 실습은 서브프로젝트 발표회다. 사전제출한 3분 데모영상을 상영하고 1인당 2분의 라이브 Q&A를 방어한다. 이 형식은 최종 시험 구술 평가(3분 데모 + 지목 구술 2분)의 마지막 리허설이며, 과제 4점 중 발표 2점(영상+Q&A)이 이날 확정된다.
14.1 발표는 검증의 마지막 단계다
완성한 도구를 청중 앞에서 실행해 보이고 그 결과의 공학적 의미를 질문에 맞서 지키는 일을 기술 발표(technical presentation)라 한다. 발표를 잘 만든 도구에 붙이는 장식으로 보기 쉽다. 그러나 검증 루틴 3종이 "이 값이 나에게 말이 되는가"를 묻는 절차라면, 발표는 "이 값이 남에게도 말이 되는가"를 묻는 절차다.
절차의 마지막 칸에 검증을 두는 관례는 이 책에서 이미 두 번 보았다. 10장의 물질수지 풀이 절차는 흐름도 그리기에서 시작해 검산(check your work)으로 끝났고, 13장에서 소개한 ML 워크플로는 "문제를 정의한다"가 1단계였다. 문제 정의로 열고 검증으로 닫는 이 순서는 그 계산을 보고하는 3분에도 그대로 적용된다.
작동하는 프로그램을 실제로 실행해 결과가 나오는 과정을 보여 주는 것을 데모(demonstration, demo)라 한다. 그리고 청중이나 채점자의 질문에 근거를 들어 답하는 것을 구술 방어라 한다. 이 수업의 발표는 이 둘의 결합이다. 슬라이드 장식 기술은 채점 대상이 아니며, 루브릭이 재는 것은 구성(문제정의→데모→화공 해석)과 Q&A 방어뿐이다.
- 문제 정의
- 분해
- 프롬프트
- 생성 코드 읽기
- 테스트
- 화공 검증
발표의 세 구간은 사이클의 축약이기도 하다. 문제정의 구간은 블록 ①을, 데모 구간은 블록 ③~⑤의 결과를, 화공 해석 구간은 블록 ⑥을 청중이 볼 수 있는 형태로 압축한다. 사이클을 성실히 돌린 프로젝트라면 발표 재료는 이미 repo 안에 있다.
14.2 3분 구성법: 문제정의 → 데모 → 화공 해석
14.2.1 시간 예산부터 세운다
발표 시간은 3분, 즉 180 s로 고정되어 있다. 배관을 설계할 때 압력강하 예산부터 세우듯, 발표도 시간 예산부터 세운다. 세 구간의 합은 다음을 만족해야 한다.
t문제정의 + t데모 + t해석 = 180 s (14.1)
권장 출발점은 문제정의 40 s, 데모 80 s, 화공 해석 60 s다. 데모에 가장 큰 몫을 주는 이유가 있다. 문제정의와 해석은 말로 압축할 수 있지만, 프로그램의 실행에는 압축할 수 없는 물리적 시간(입력, 실행, 출력 확인)이 들기 때문이다. 이 배분은 출발점이고, 확정은 리허설 실측으로 한다. 대본을 소리 내어 읽고 구간별 시간을 재서, 20 s 이상 초과한 구간을 먼저 줄인다.
14.2.2 문제정의 구간: 청중이 궁금한 것은 폴더 구조가 아니다
첫 40 s의 임무는 "이 도구가 없으면 무엇이 귀찮거나 위험한가"를 한 문장으로 세우고, 도구의 입력과 출력을 선언하는 것이다. 화공양론 숙제 검산기라면 "recycle이 있는 물질수지는 손 검산에서 스트림 하나를 빼먹기 쉽다. 이 도구는 흐름도 텍스트를 받아 정합성 위반 스트림을 출력한다"가 전부다.
가장 흔한 실수는 파일 구조와 사용 라이브러리 소개로 시작하는 것이다. 그것은 README의 몫이고 첫 40 s의 몫은 아니다. 문제가 서지 않으면 그 도구가 왜 필요한지도 청중에게 전달되지 않는다.
14.2.3 데모 구간: 실행 1사이클 + 검증 1개
데모 80 s에 넣을 것은 두 가지다. 입력→실행→출력의 완결된 1사이클, 그리고 검증 루틴이 통과하는 순간 1개다. 예를 들어 T-xy 도구라면 조성 입력, 다이어그램 출력까지 보여 준 뒤, 순물질 극한(x=1)에서 곡선이 문헌 끓는점에 닿는 화면을 잡아 준다. 기능 다섯 개를 스치듯 훑는 것보다 한 사이클을 검증까지 완주하는 편이 루브릭 구성 5점에 직결된다.
데모 준비의 고전적 함정은 경로다. 본인 노트북의 작업 폴더에서는 돌던 코드가 발표용 환경에서 이렇게 죽는다.
FileNotFoundError: [Errno 2] No such file or directory: 'data.csv'
원인은 상대경로다. 스크립트를 어느 폴더에서 실행하느냐에 따라 'data.csv'가 가리키는 위치가 달라진다. 녹화 전에 데이터 경로를 스크립트 위치 기준으로 고정해 두면 실행 위치와 무관해진다.
from pathlib import Path
BASE = Path(__file__).resolve().parent # 이 스크립트가 있는 폴더
DATA = BASE / "data.csv" # 실행 위치와 무관하게 고정된 경로
그래도 실패에는 대비한다. 실행 화면 캡처 2~3장을 예비 슬라이드로 준비해 두면, 라이브 실행이나 영상 재생이 죽어도 데모 구간의 서사는 이어 갈 수 있다. 백업이 있으면 장애가 나도 침착하게 대응할 수 있고, 그 침착함이 Q&A 3점으로 이어진다.
14.2.4 화공 해석 구간: 숫자를 상식과 문헌에 잇는다
마지막 60 s는 출력 수치의 공학적 의미를 말하는 시간이다. 여기서 하는 일은 8장에서 훈련한 것과 같다. Antoine 상관식으로 계산한 물의 증기압이 "물은 1 atm에서 100 °C에 끓는다"는 상식 대조값과 크게 어긋나면서 유효범위 밖 외삽이 드러났듯, 해석 구간은 데모의 숫자를 대조값·유효범위·한계와 연결한다. "계산이 끝났다"와 "결과를 믿을 수 있다" 사이의 거리를 메우는 것이 이 구간의 임무다.
한계를 말하는 것을 두려워할 이유가 없다. 최종 시험 루브릭의 코드 이해 10점 중 3점이 "한계·개선점"에 걸려 있다. 자기 도구의 유효범위를 먼저 밝히면 그 3점을 스스로 확보할 수 있다. 감추면 같은 내용을 질문으로 되돌려 받게 된다.
예제 14.2-1 도구⑨ 공정 데이터 대시보드의 3분 발표 구성
13주차에 만든 도구⑨(CSV→인터랙티브 공정 대시보드)를 서브프로젝트로 확장했다. 식 (14.1)의 예산을 만족하는 3분 발표 구성안을 설계하고, 시간 합계를 코드로 확인하라.
구간 설계와 내용 선정은 사람의 일이다(사이클 ①). AI에게는 두 가지만 맡긴다. 시간 예산 검산 코드 생성과 초안 대본의 압축이다. 대본의 수치와 주장은 AI가 바꾸지 못하게 프롬프트에 명시한다.
# 3분 발표 시간 예산 — 단위: 초(s)
budget = {"문제정의": 40, "데모": 80, "화공 해석": 60}
total = sum(budget.values()) # s
assert total == 180, f"합계 {total} s — 180 s에 맞춰라"
for part, t in budget.items():
print(f"{part}: {t} s ({t / 180:.0%})") # 구간별 비중 출력
아래 발표 대본 초안을 소리 내어 읽으면 데모 구간이 100초가 넘어. 80초 분량으로 줄여 줘. 단, 수치와 검증 관련 문장은 한 글자도 바꾸지 마. [대본 붙여넣기]
"…CSV를 올리면 대시보드가 유량·온도 추이를 그리고, 3개 열의 이상치 후보를 표시합니다. 화면 오른쪽 검증 탭에서 정상 조업 구간 평균이 조업일지 기록값과 일치하는 것을 보십시오…" (압축된 대본, 이하 생략)
✔ 수치·검증 문장 원문 유지 확인(diff로 대조). 소리 내어 읽어 실측 78 s로 예산 안에 들어왔다. 다만 AI가 화면 전환을 알리는 문장을 잘라냈으므로, 데모 화면과 말이 어긋나지 않게 전환 문장 하나를 되살렸다.
- 단위 체크: 모든 구간 값이 s 단위, 합 180 s = 3 min(발표 규정과 일치)
- 대조값: 루브릭 "문제정의→데모→화공 해석 3분 구성 5점"의 세 요소가 구성안에 전부 존재
- 극한값: 데모 재생이 죽는 극한(t데모→0)에서도 캡처 백업으로 서사가 이어지도록 예비 슬라이드 준비 확인
분석 발표 설계에서 AI에게 맡길 수 있는 것은 압축과 검산이다. 구간 배분과 검증 장면의 선택은 프로젝트를 아는 사람만 할 수 있다. 본인 프로젝트로 budget을 다시 채우고, 데모 구간을 90 s로 늘리면 어느 구간에서 10 s를 회수할지 정해 다시 돌려 보라.
스스로 점검 (해답은 권말)
- 식 (14.1)에서 데모 구간을 100 s로 늘리기로 했다. 문제정의와 화공 해석 중 어느 쪽을 줄이는 것이 루브릭상 덜 위험한가? 근거를 한 문장으로 쓰라.
- 데모 80 s 안에 반드시 화면으로 보여야 할 두 가지는 무엇인가?
- 대본 속 수치의 출처가 되어야 할 문서는 무엇인가?
14.3 청중 질문 네 유형과 방어
14.3.1 질문은 네 유형뿐이다
발표 뒤의 2분은 준비한 정도가 그대로 드러나는 시간이다. 다행히 기술 발표에 들어오는 질문은 대부분 네 유형으로 정리되고, 각 유형은 최종 시험 루브릭의 특정 배점과 일대일로 대응한다. 유형을 알면 방어를 미리 준비할 수 있다.
| 유형 | 전형적 질문 | 루브릭 대응 | 방어의 근거 |
|---|---|---|---|
| 사실 확인형 | "그 값은 어디서 나왔나?" | 작동성: 검증 증빙(README 기록 필수) | README의 검증 기록·출처(NIST 등)를 짚는다 |
| 지목 설명형 | "이 줄은 무슨 일을 하나?" | 코드 이해: 무작위 지목 설명 4점 | EiPE 다섯 요소 2~3문장(5장) |
| 변형 예측형 | "이 줄을 바꾸면 어떻게 되나?" | 코드 이해: 예측 3점 | 파라미터를 바꿔 돌려 본 What-if 경험 |
| 한계·개선형 | "어디까지 믿을 수 있나?" | 코드 이해: 한계·개선점 3점, 발표 Q&A 3점 | 유효범위·가정을 먼저 선언해 둔 해석 구간 |
표의 오른쪽 열에서 보듯, 모든 답은 가리킬 수 있는 근거(코드의 줄, README의 절, 커밋 이력, 문헌값)로 끝난다. "그럴 겁니다"로 끝나는 답은 방어가 되기 어렵다. 근거가 없으면 "확인하지 못했다, 한계로 기록하겠다"가 정답이며, 이는 한계·개선형 답변으로 부분 점수를 지킨다.
14.3.2 "설명 불능 = 0점" 규정의 재확인
1주차 역할 계약을 다시 꺼낸다. AI가 코드를 쓰는 대신, 학생은 문제 분해·생성 코드 읽기·화학공학적 검증을 책임지며, "설명 못 하면 이해 점수 0점"이다. 최종 시험 루브릭은 이것을 조작적으로 정의해 두었다. 코드 이해 10점 차원에서 지목 질문 3개 중 2개 이상 설명에 실패하면 이 차원은 0점이다. 9점도 5점도 아닌 0점인 이유는, 설명하지 못하는 코드를 부분적으로 이해한 코드로 보기 어렵기 때문이다.
이 규정 앞에서 발표회는 안전한 실패 기회다. 오늘 Q&A에서 막힌 질문은 시험 전에 미리 발견한 구멍인 셈이다. 막힌 자리를 위키에 기록하는 것이 이번 주 위키 과제의 두 번째 항목인 이유다.
14.3.3 예상 질문은 AI로 미리 생성한다
방어 준비에서 AI의 쓸모는 대본 작성이 아니라 모의 청중 역할이다. 본인 코드를 주고 질문을 시켜 보자.
다음은 내 서브프로젝트의 핵심 모듈이다. [코드 붙여넣기] 화학공학과 교수가 발표 Q&A에서 물을 법한 질문 5개를 만들어 줘. 각 질문마다 내 코드의 어느 부분이 답의 근거가 되는지도 짝지어 줘.
1. "기포점 반복 계산의 수렴 판정 기준은 몇으로 잡았고 왜 그 값인가?" (근거: solve_bubble()의 tol 인자) … 4. "이 도구를 비이상 용액에 쓰면 어떻게 되는가?" (근거: README 한계 절) … 5. "반응이 있는 계로 확장하려면 반응속도식을 어디에 넣어야 하는가?" (근거: 해당 없음)
✔ 5개 중 4개는 표 14-1의 유형에 정확히 떨어진다. 답변 근거를 README와 코드 줄 번호로 준비했다. 5번은 반응속도론(3학년 범위)이라 이 수업 범위 밖이므로 "본 도구의 범위 밖이며, 확장 지점만 한계 절에 기록했다"로 답하기로 정리. AI가 만든 질문 목록도 그대로 믿지 않고 범위를 검증했다.
생각해보기: 이 수업의 발표회는 라이브 데모 대신 사전 녹화 영상을 상영한다. 이 선택이 없애 주는 위험 한 가지와 새로 만드는 위험 한 가지를 각각 제안하고, 후자를 어떻게 보완할지 말해 보라.
스스로 점검 (해답은 권말)
- "이 함수의 수렴 기준을 10배 느슨하게 하면 결과가 어떻게 변하나?"는 표 14-1의 어느 유형인가?
- 지목 질문 3개 중 1개만 설명에 성공했다. 코드 이해 차원의 점수는 몇 점인가?
- 답의 근거로 가리킬 수 있는 것 네 가지를 나열하라.
14.4 프로젝트 완성 체크리스트
14.4.1 채점자는 README부터 연다
발표가 3분이라면 채점은 그 몇 배의 시간 동안 repo 위에서 이루어진다. 채점자가 처음 여는 문서는 README고, 루브릭 작동성 14점의 첫 5점이 "README대로 실행"이다. 다른 컴퓨터에서 README의 지시만 따라 같은 결과가 나오는 성질을 재현성(reproducibility)이라 한다. 재현성은 문서의 품질로 결정된다.
이 수업의 README에 반드시 있어야 할 절은 네 개다. 첫째, 실행법이다. 설치와 실행 커맨드를 복사해 붙일 수 있는 형태로 적는다. 둘째, 검증 증빙이다. 단위 체크·대조값·극한값 3종의 기록으로, 루브릭이 필수 항목으로 정해 둔 부분이다. 셋째, 재사용 도구다. 서브프로젝트 공통 요건인 "본인의 사다리 산출물 2개 이상 재사용"을 어느 도구로 충족했는지 명시한다. 넷째, AI 사용내역 선언문이다. 모든 평가물에 1쪽 첨부가 의무이며 미첨부는 부정행위로 처리된다.
| 점검 항목 | 확인 방법 | 루브릭 대응 |
|---|---|---|
| README대로 실행 재현 | 새 폴더에 클론 → README 커맨드만으로 실행 | 작동성: 실행 5점 |
| 핵심 기능 1사이클 완주 | 입력→실행→출력 데모 시나리오와 일치 | 작동성: 핵심 기능 6점 |
| 잘못된 입력 처리 | 빈 파일·범위 밖 값 입력 시 안내 후 종료 | 작동성: 예외 처리 3점 |
| 검증 증빙 3종 기록 | README 검증 절에 단위·대조값(출처 포함)·극한값 | 작동성 필수 항목 |
| 재사용 도구 2개 이상 명시 | README에 도구 번호와 import 위치 기록 | 서브프로젝트 공통 요건 |
| AI 사용내역 선언문 1쪽 | 선언문 파일 존재 + 제출물에 첨부 | 미첨부 = 부정행위 처리 |
| 커밋 이력이 과정을 증언 | 기능 단위 커밋, 의미 있는 메시지 | 과정 채점·포렌식 대조 |
표 14-2는 사람이 눈으로 훑는 용도지만, 앞의 네 항목은 기계로도 점검할 수 있다. 점검을 스크립트로 만들어 두면 최종 시험 당일 종료 직전에도 30초 만에 돌릴 수 있다.
예제 14.4-1 README 완성도 점검 스크립트
repo 폴더를 받아 README.md에 필수 절(실행법·검증 증빙·재사용 도구·AI 사용내역)이 있는지 점검하고 항목별 통과/누락을 출력하는 스크립트를 만들라.
필수 항목의 목록과 표제 규약은 사람이 정한다(분해·명세는 학생 몫). 파일을 읽고 대조하는 코드는 AI에게 맡기고, 생성 코드는 일부러 망가뜨린 README로 검증한다.
파이썬 함수를 만들어 줘. 폴더 경로를 받아 그 안의 README.md를 utf-8로 읽고, 딕셔너리로 준 필수 표제들이 본문에 있는지 항목별 True/False로 돌려줘. 표준 라이브러리만 쓰고, 주석은 한국어로.
# README 완성도 점검기 — 시험3 루브릭 "작동성" 대비
from pathlib import Path
REQUIRED = { # 점검 항목: README에 있어야 할 표제
"실행법": "## 실행법",
"검증 증빙": "## 검증",
"재사용 도구": "## 재사용",
"AI 선언문": "## AI 사용내역",
}
def check_readme(repo_dir):
"""repo_dir의 README.md에서 필수 절 유무를 점검한다."""
text = Path(repo_dir, "README.md").read_text(encoding="utf-8")
return {name: (head in text) for name, head in REQUIRED.items()}
for name, ok in check_readme(".").items(): # 프로젝트 repo에서 실행
print(f"{name}: {'통과' if ok else '누락'}")
✔ 표준 라이브러리(pathlib)만 사용, 인코딩 utf-8 명시 확인. 검증 절 표제를 일부러 지운 README로 실행 → "검증 증빙: 누락" 출력 확인. 단 README.md가 없는 폴더에서는 예외로 죽는다. (아래 극한값 항목 참조.)
- 단위 체크: 물리량 없음(텍스트 처리). 대신 인코딩(utf-8) 명시로 환경 간 재현성 확보
- 대조값: 정답을 아는 입력으로 시험. 절 하나를 지운 README에서 정확히 그 항목만 "누락"으로 출력
- 극한값: README.md가 아예 없는 극한 →
FileNotFoundError발생. 예외 처리 미비(연습문제 P14.1에서 보강)
분석 점검기 자체도 극한값 검증을 통과하지 못하면 미완성이다. 도구를 점검하는 도구에도 같은 루틴이 적용된다는 것이 이 예제의 교훈이다. 표제 규약을 본인 README에 맞게 REQUIRED만 바꿔 그대로 재사용하라. 프롬프트에 "표제가 아니라 절 내용의 존재까지 확인해 줘"를 더해 재생성해 보는 것도 좋은 What-if다.
14.4.2 최종 시험 규정 브리핑
발표회가 끝나면 남은 것은 15주차 최종 시험(40점)뿐이므로, 운영 규정을 여기서 마지막으로 확인한다. 주제는 당일 공개되며 전원 동일 구조에 학번별 파라미터가 배정된다. 시작 시 빈 repo 첫 커밋이 의무고, 30분마다 스냅샷 커밋을 남기며, 2시간 내 커밋 5회 이상을 채워야 한다. 종료 즉시 업로드하면 결과물은 동결된다. 사후 수정분은 채점에서 제외된다.
당일 장애에 대비한 비상 프로토콜은 강의계획서와 부록 E에 명문화되어 있고, 15장에서 유형별 대응으로 다시 다룬다. 여기서 할 일은 30분 스냅샷 커밋을 규정대로 남겨 두는 것이다. 그러면 장애가 나도 그때까지의 작업이 부분 채점으로 인정된다. 30분 커밋은 규정이기 이전에 보험인 셈이다.
14.5 실습 14: 서브프로젝트 발표회 (120분)
준비물과 이전 도구 재사용
- 사전제출 완료된 3분 데모영상 (제출함 확인)
- 서브프로젝트 repo(발표 시점 기준 최종 커밋 완료 상태)
- 본인 README 표제 규약에 맞게 수정한 코드 14-3 점검기
- 표 14-1을 인쇄하거나 화면에 띄워 둘 것 (동료 리뷰에 사용)
과제
- 실습 14.1: 최종 점검 (개인, 15분) 코드 14-3 점검기를 본인 repo에서 실행하고, 네 항목 전부 "통과"가 나오게 README를 보수한 뒤 커밋하라. (점검기 출력 4행이 모두 통과면 완료)
- 실습 14.2: 발표 방어 (1인당 5분) 본인 차례에 데모영상 3분 상영 후 라이브 Q&A 2분을 방어하라. 모든 답을 근거(코드 줄·README 절·커밋·문헌값)로 끝내라. (지목 질문에 EiPE 다섯 요소 형식으로 답했으면 완료)
- 실습 14.3: 동료 리뷰 (발표회 내내) 다른 발표 2건 이상에 대해 표 14-1의 유형을 명시한 질문을 1개씩 던지거나 기록하라. 발표자와 청중이 서로를 훈련시키는 것을 동료 리뷰(peer review)라 한다. (질문 2건과 유형 표기가 메모에 남았으면 완료)
- 실습 14.4: 방어 기록 (퇴실 전 15분) 오늘 받은 질문 전부와, 그중 답하지 못한 것 1건 이상을 위키에 기록하고 커밋하라. (커밋이 GitHub에 보이면 완료)
완성 기준
- 데모영상 사전제출 + README 점검기 4항목 통과 커밋
- Q&A 2분 방어 완료(지목 질문 응답에 근거 제시)
- 동료 질문 2건 이상, 유형 표기와 함께 기록
- 위키 방어 기록 커밋 1건 이상
- 과제 4점 중 발표 2점(영상+Q&A)이 오늘 채점된다. 결과물 2점(작동+검증 증빙)은 repo 기준이다
막혔는가?
- README가 허술하면 이렇게 요청하라. "내 README를 붙여넣는다. 최종 시험 루브릭의 작동성 항목(README대로 실행 / 핵심 기능 / 예외 처리 / 검증 증빙) 기준으로 부족한 절을 지적하고, 무엇을 추가할지 물어봐 달라."
- 예상 질문이 안 떠오르면 이 프롬프트로 시작하라. "내 발표 대본과 핵심 모듈이다. 표의 네 유형(사실 확인·지목 설명·변형 예측·한계)별로 질문을 1개씩 만들고, 답의 근거가 될 코드 위치를 짝지어 달라."
- 지목 설명이 불안하면 "이 함수를 한국어 2~3문장으로 설명해 달라"고 요청한 뒤, 그 설명을 보지 않고 본인 문장으로 다시 말해 보라. 두 설명이 다른 지점이 본인이 모르는 지점이다.
퇴실 전 위키 기록: 오늘 만든 것(발표)·막힌 것(답 못 한 질문)·틀린 것(대본이나 README에서 고친 것) 1건 이상을 커밋해야 퇴실한다.
요약
- S 14-1 발표 시간 예산: t문제정의 + t데모 + t해석 = 180 s, 곧 식 (14.1)이다. 출발점은 40/80/60 s이고, 확정은 소리 내어 읽는 실측으로 한다.
- S 14-2 방어의 원칙: 모든 답은 가리킬 수 있는 근거(코드 줄·README 절·커밋·문헌값)로 끝낸다. 근거가 없으면 한계로 옮겨 기록한다.
- S 14-3 README 필수 4절: 실행법 / 검증 증빙 3종 / 재사용 도구 2개 이상 / AI 사용내역 선언문. 이 네 절이 지목 질문 3개 중 2개 이상 설명 실패 시 코드 이해 차원 0점 규정과 함께 채점의 골격을 이룬다.
| 항목 | 발표회 (14주) | 최종 시험 구술 평가 (15주+시험주간) |
|---|---|---|
| 형식 | 3분 데모영상 사전제출 + Q&A 2분 | 1인 3분 데모 + 지목 구술 2분 |
| 주제 | 본인이 선언한 서브프로젝트 (9주) | 당일 공개 + 학번별 파라미터 |
| 배점 | 과제 4점 중 발표 2점 (결과물 2점 별도) | 40점 중 발표(구술 평가) 8점, 코드 이해 10점 연동 |
| 실패의 값 | 구멍을 사전 발견해 위키에 기록 | 성적 확정 |
| 역할 | 마지막 리허설 | 실전 |
도구 사다리 아홉 칸이 다 찼고, 그 도구들을 엮은 서브프로젝트가 오늘 청중 앞을 통과했다. AI 협업 사이클로 보면 이번 주는 ④ 생성 코드 읽기와 ⑥ 화공 검증을 혼자만의 루틴에서 공개 방어로 끌어올린 주다. 오늘 답하지 못한 질문이 있었다면 그것이 15주차 전에 받은 채점표다. 남은 것은 같은 형식을 2시간으로 늘린 실전 하나뿐이다.
용어 정리
- 기술 발표 (technical presentation)
- 완성한 도구를 청중 앞에서 실행해 보이고 결과의 공학적 의미를 질문에 맞서 지키는 일.
- 데모 (demonstration, demo)
- 작동하는 프로그램을 실제로 실행해 결과가 나오는 과정을 보여 주는 것.
- 구술 방어
- 청중이나 채점자의 질문에 근거를 들어 답하는 것. 최종 시험에서는 지목 구술 2분으로 운영된다.
- 재현성 (reproducibility)
- 다른 컴퓨터에서 README의 지시만 따라 같은 결과가 나오는 성질.
- 동료 리뷰 (peer review)
- 발표자와 청중이 질문과 피드백으로 서로의 산출물을 검토하고 훈련시키는 활동.
연습문제
Q군: 개념·읽기 (AI 없이 풀라)
- Q14.1 다음 발표 대본 문장 세 개에서 검증 없는 단정을 각각 찾아 밑줄 긋고, 근거를 제시하는 문장으로 고쳐 쓰라. ⓐ "이 도구는 어떤 물질에도 정확합니다." ⓑ "T-xy 곡선의 양 끝값을 문헌 끓는점과 대조해 차이를 README에 기록했습니다." ⓒ "예외 처리는 완벽하게 되어 있습니다."
- Q14.2 최종 시험 코드 이해 차원(10점)에서 지목 질문 3개 중 2개 이상 설명에 실패하면 점수가 어떻게 되는지 쓰고, 이 규정이 1주차 역할 계약의 어느 조항을 집행하는 장치인지 설명하라.
- Q14.3 일부러 구술 평가에서 0점에 가까운 발표를 만드는 방법 세 가지를 제시하고, 각각이 루브릭의 어느 차원(작동성·코드 이해·프롬프트 전략·발표)을 무너뜨리는지 밝혀라.
P군: 제작·계산 (AI 사용 전제)
- P14.1 코드 14-3 점검기에 README.md가 없는 극한을 처리하는 예외 처리를 추가하라.
FileNotFoundError가 나면 프로그램이 죽는 대신 "README 없음: 작동성 5점 위험"을 출력하고 정상 종료해야 한다. - P14.2 본인 서브프로젝트의 3분 대본을 작성하고, 식 (14.1)의 예산과 소리 내어 읽은 실측 시간을 구간별로 표로 비교하라. 20 s 이상 초과한 구간을 AI에게 압축시키되, 예제 14.2-1의 프롬프트처럼 수치·검증 문장 변경을 금지하고 diff로 확인하라.
- P14.3 지목 구술 모의시험기를 만들라. 본인 repo의 .py 파일에서 빈 줄과 주석을 제외한 무작위 한 줄을 골라 보여 주는 도구다. 페어와 교환해 서로 5회씩 지목 시험을 치르고, 설명에 실패한 줄과 그 이유를 위키에 기록하라. (사다리 산출물의 파일 읽기 코드를 재사용할 것)
위키 기록 과제 (이번 주 의미 커밋 ≥1이 채점 지표다)
- 이 장 핵심 1페이지 정리: 3분 구성과 질문 네 유형 표를 본인 언어로 재구성해
#seed로 기록하라. - "내가 틀렸던 것" 1건: 오늘 Q&A에서 답하지 못했거나 근거 없이 답한 질문을 원문 그대로 적고, 지금 찾은 정답과 근거를 붙여라.
- 타 과목 연결 1건: 화공양론·유체역학 수업에서 결과를 발표하거나 보고서로 낼 때 이 장의 3분 구성을 어떻게 옮겨 쓸지 기록하라.
15장 최종 실기: 2시간의 전략
이번 주의 산출물: 최종 시험 repo, 그리고 계속 쓸 학습 OS
다음 상황을 생각해 보자. 15주차 실습 시간, 전산실습실의 프로젝터에 주제가 떠 있다. "학번별 이성분계의 기포점/이슬점 계산기와 T-xy 작도." 화면 아래에는 각자 내려받을 파라미터 파일의 경로가 적혀 있다.
학생 A는 곧바로 채팅창을 열고 "기포점 계산기 전체를 만들어 줘"라고 입력한다. 150줄짜리 코드가 쏟아지고, 첫 실행에서 에러가 난다. 어디를 고쳐야 할지 몰라 같은 프롬프트를 조금씩 바꿔 다시 던진다. 1시간이 지나도록 repo에는 커밋이 하나뿐이다.
학생 B는 다르게 출발한다. 먼저 빈 repo에 첫 커밋을 남기고, 배부된 파라미터 파일을 열어 키 이름과 단위를 확인한다. 종이에 함수 세 개짜리 설계를 그리고, 15분이 지나서야 첫 프롬프트를 보낸다. 30분 뒤 스냅샷 커밋 시각, B의 repo에는 이미 돌아가는 최소 버전이 올라가 있다.
두 사람을 가른 것은 코딩 실력이 아니다. 14주 동안 연습한 절차를 시험장에서 그대로 재생했는가의 차이다. 이 장은 그 절차를 규정, 배점, 시간의 언어로 정리한다.
이 장에서 다루는 것
- 최종 시험(시험3) 운영 규정(당일 주제, 학번별 파라미터, 커밋 규정, 동결, 구술 평가)을 조문과 이유로 해부한다
- 루브릭 4개 차원(작동성 14 / 코드 이해 10 / 프롬프트 전략 8 / 발표 8)의 점수 구조를 읽는다
- 2시간 시간 배분 모델과 '작동 우선' 원칙을 세운다
- 버그·장애 상황의 비상 프로토콜을 훈련한다
- 수업이 끝난 뒤 학습 OS를 유지·확장하는 계획을 세운다
학습목표 이 장을 마치면 다음을 할 수 있다.
- 최종 시험의 커밋 규정 네 가지(빈 repo 첫 커밋, 30분 스냅샷, 2시간 내 5회 이상, 종료 즉시 동결)를 순서대로 수행할 수 있다.
- 루브릭 4개 차원의 배점 구조를 설명하고, 가상의 제출물을 루브릭으로 채점할 수 있다.
- 당일 공개된 주제를 15분 안에 함수 단위로 분해하고 우선순위를 정할 수 있다.
- 남은 시간에 따라 기능 추가와 검증·제출 사이의 판단을 내릴 수 있다.
- 버그·장애 상황에서 비상 프로토콜에 따라 부분 채점 근거를 확보할 수 있다.
- 검증 루틴 3종과 위키 운영을 수업 이후의 과목과 업무로 확장하는 계획을 세울 수 있다.
이번 주 2시간은 실습이 아니라 최종 시험 본편이다. 지난 14주의 매 실습이 이 형식의 축소판이었고, 12주차 시험2가 같은 형식의 리허설이었다. 오늘 새로 배우는 것은 없다. 배운 것을 시간 안에 재생하는 날이다.
15.1 최종 시험 규정: 조문과 이유
시험 규정을 처음 읽으면 통제 장치의 목록으로 보인다. 그러나 조문 하나하나는 학생의 점수를 지키도록 설계되어 있다. 빈 repo 첫 커밋은 시작 시각의 증거가 되고, 30분 스냅샷은 장애가 나도 그때까지의 작업을 인정받게 한다. 규정을 이유와 함께 읽어 두면 시험장에서 규정 때문에 당황할 일이 없다.
표 15-1은 최종 시험 규정 전체를 조문과 이유의 쌍으로 정리한 것이다.
| 조문 | 내용 | 왜 이렇게 정했는가 |
|---|---|---|
| 주제 | 당일 공개, 전원 동일 구조 + 학번별 파라미터(물질계·조업조건·데이터 시드 상이) | 사전 제작·복제를 원천 무효화하면서 주제 간 난이도 등가성을 지킨다 |
| 범위 | 8~13주 도구 사다리 범위 내 | 가르친 것만 평가한다 |
| 첫 커밋 | 시작 시 빈 repo 첫 커밋 의무 | 커밋 포렌식의 원점이 되는 시작 시각 기준점 |
| 스냅샷 | 30분마다 스냅샷 커밋 | 장애 시 부분 채점의 근거 |
| 커밋 수 | 2시간 내 커밋 5회 이상 | 완성본 한 방 제출을 차단하는 과정의 증거 |
| 동결 | 종료 즉시 업로드, 사후 수정분은 채점 제외 | 제출 후 수정으로 인한 변별 오염 방지 |
| 구술 평가 | 시험주간에 1인 3분 데모 + 지목 구술 2분 | 코드 이해 검증, 대리 제출 차단 |
| 선언문 | AI 사용내역 선언문 1쪽 첨부, 미첨부는 부정행위 처리 | 모든 평가물 공통 규정으로 AI 사용을 투명화 |
| 위키 | 본인 위키 참조 허용 + 활용 가점 | 15주 학습 OS를 시험장 자산으로 인정 |
15.1.1 당일 주제와 학번별 파라미터
주제는 당일 공개된다. 전원이 같은 구조의 문제를 받되 물질계, 조업조건, 데이터 시드는 학번별 파라미터로 달라진다. 옆 사람의 코드를 그대로 가져오면 실행은 되지만 답이 틀리는 구조다. 그래서 시험장에서 유일하게 쓸모 있는 외부 자료는 남의 코드가 아니라 본인의 도구 사다리와 위키다.
1주차에 배포된 규정이 예시하는 주제 유형은 세 가지다. 학번별 이성분계의 기포점/이슬점 계산기와 T-xy 작도(NIST 대조 검증 포함), 학번별 recycle 공정의 물질수지 정합성 검사와 흐름 시각화, 그리고 학번별 실험 데이터 회귀와 이상치 자동 탐지 리포트 생성기. 각각 9장, 10장, 12~13장의 도구가 코어 부품이다. 어느 유형이 나와도 이미 한 번 만들어 본 것의 변형이다.
15.1.2 커밋 규정과 커밋 포렌식
채점자는 코드만 읽지 않는다. 커밋 이력에서 작업의 시간 구조를 읽는다. 이를 커밋 포렌식(commit forensics)이라 한다. 커밋 이력이 어떻게 읽히는지, 두 제출물의 로그를 나란히 놓고 보자.
git log --pretty=format:"%h %ad %s" --date=format:"%H:%M" --reverse)# 제출물 ㄱ
9f3a1c2 14:00 시험 시작 (빈 커밋)
5b7e881 14:12 계획: PLAN.md — 함수 3개로 분해
02c9df4 14:31 스냅샷: antoine 함수 + 대표값 1건 확인
77aa310 15:02 스냅샷: 기포점 이분법, x=1 극한 검증 통과
c4d19e7 15:29 스냅샷: T-xy 작도 초안, README에 검증 기록
e8b02f5 15:55 최종: 예외 처리 + AI 사용내역 선언문
# 제출물 ㄴ
a11f9c0 15:58 완성 (파일 12개, +512줄)
제출물 ㄱ은 커밋 6회, 30분 간격 스냅샷, 메시지마다 그 시점의 작업이 드러난다. 제출물 ㄴ은 커밋 1회뿐이다. 커밋 5회 규정 위반이면서, 첫 커밋이 곧 완성본이라 자동으로 플래그가 붙는 유형이다. 플래그는 곧바로 부정행위 판정이 아니라 구술 평가에서의 소명 대상이지만, 지목 구술에서 본인 코드를 설명하지 못하면 소명의 길이 없다.
스냅샷 커밋의 다른 얼굴은 보험이다. 노트북이 죽거나 네트워크가 끊겨도, 마지막 스냅샷까지의 작업은 부분 채점(partial credit)의 근거로 인정된다. 30분마다 커밋하라는 조문은 감시가 아니라 학생 쪽의 안전장치다.
스스로 점검
- 시험 종료 10분 뒤 치명적 버그를 발견했다. 지금 고쳐서 push하면 채점에 반영되는가? 근거 조문은 무엇인가?
- 30분 스냅샷 커밋은 어떤 상황에서 점수를 지켜 주는가? 두 가지 상황을 말하라.
15.2 루브릭 해부: 40점은 어디서 오고 어디서 사라지는가
루브릭은 1주차에 전문이 배포되었다. 15주 내내 같은 표였으므로 암기가 아니라 해석이 남은 과제다. 표 15-2에 전문을 다시 싣는다.
| 차원 | 배점 | 세부 |
|---|---|---|
| 작동성 | 14 | README대로 실행 5 / 핵심 기능 6 / 예외 처리 3. 검증 증빙(단위·대조값·극한값의 README 기록) 필수 항목 |
| 코드 이해 | 10 | 무작위 지목 설명 4 / "이 줄을 바꾸면?" 예측 3 / 한계·개선점 3. 지목 질문 3개 중 2개 이상 설명 실패 시 이 차원 0점 |
| 프롬프트 전략 | 8 | 문제 분해·반복 개선 4 / 디버깅 프롬프트 2 / 검증 습관 2. 게이트웨이 서버측 로그 기준(학생 제출 로그는 대조용) |
| 발표(구술 평가) | 8 | 문제정의→데모→화공 해석 3분 구성 5 / Q&A 3 |
15.2.1 작동성 14점: 채점자는 README만 읽는다
"README대로 실행 5점"의 의미를 정확히 새겨야 한다. 채점자는 저자가 아니다. 자동 스모크 테스트 하네스가 README에 적힌 실행 방법을 문자 그대로 따라 돌린다. README에 없는 암묵지(특정 폴더에서 실행해야 한다든가, 파라미터 파일을 먼저 옮겨야 한다든가)는 실행 실패로 기록된다. 실행이 안 되면 핵심 기능 6점을 증명할 길도 함께 사라진다.
검증 증빙이 필수 항목이라는 조건도 여기 걸려 있다. 단위 체크, 대조값, 극한값의 확인 결과가 README에 기록되어 있지 않으면 작동성 차원에서 만점이 불가능하다. 코드가 돌아간다는 사실과 결과가 옳다는 증거는 별개이고, 이 수업은 증거 쪽에 점수를 준다.
15.2.2 코드 이해 10점: 0점 게이트가 있는 유일한 차원
구술 평가에서 채점자가 repo의 아무 줄이나 지목해 설명을 요구한다. 세 번 지목해서 두 번 이상 설명하지 못하면 이 차원 전체가 0점이다. 이 조작적 정의는 1주차부터 공지된 것으로, "돌아가는데 설명 못 함"을 걸러 내는 이 수업의 마지막 관문이다. "이 줄을 바꾸면 출력이 어떻게 되는가"라는 예측 3점은 시험1의 출력 예측 문항과 같은 유형이다. AI 없이 남긴 개인 두뇌를 여기서 다시 쓴다.
15.2.3 프롬프트 전략 8점: 시험 중의 습관이 그대로 채점된다
이 차원의 채점 원본은 학생이 제출하는 파일이 아니라 게이트웨이 서버측 로그다. 시험이 끝난 뒤 잘 꾸민 기록으로 만회할 수 없고, 시험 중에 실제로 어떻게 물었는가가 전부다. 문제를 함수 단위로 분해해 요청하고, 생성 결과의 결함을 짚어 개선을 요구한 흔적이 4점을 만든다. 에러 메시지를 붙여 원인을 좁혀 간 대화가 2점, 대조값을 확인하는 질문이 2점이다. 14주 동안의 습관이 있다면 따로 준비할 것이 없는 차원이다.
15.2.4 발표 8점, 그리고 배점이 말하는 전략
구술 평가는 시험주간에 1인 3분 데모와 지목 구술 2분으로 진행된다. 3분 구성은 14장에서 훈련한 문제정의→데모→화공 해석 그대로다. 발표 5점에 Q&A 3점이다. 서브프로젝트 발표회가 이 형식의 리허설이었다.
배점 구조를 계산해 보면 전략이 나온다. 가상의 두 제출물을 루브릭으로 채점한 결과가 표 15-3이다.
| 차원(배점) | 제출물 ㄷ: 기능 전부 작동, 설명 불능 | 제출물 ㄹ: 핵심만 작동, 검증·설명 충실 |
|---|---|---|
| 작동성 (14) | 11: 실행 5, 기능 6, 예외 0, 검증 증빙 누락(필수 항목 미충족) | 12: 실행 5, 기능 4, 예외 3, 증빙 완비 |
| 코드 이해 (10) | 0: 지목 3문 중 2문 실패, 게이트 발동 | 9: 지목 전부 설명, 한계 서술 충실 |
| 프롬프트 전략 (8) | 4: 통째 요청 반복, 검증 프롬프트 없음 | 7: 분해·개선·대조 흔적 |
| 발표 (8) | 5: 데모는 성공, Q&A 침묵 | 7 |
| 합계 (40) | 20 | 35 |
기능을 더 많이 완성한 쪽의 총점이 15점 낮다. 이해를 포기한 채 기능만 늘리는 전략은 코드 이해 게이트 하나로 점수를 잃는다. 반대로 기능이 3분의 2뿐이어도 검증 증빙과 설명이 갖춰지면 30점대가 나온다. 이 산술이 다음 절의 시간 배분 전략을 결정한다.
생각해보기 루브릭의 "예외 처리 3점"을 2시간 안에 버는 가장 값싼 방법은 무엇인가? 입력값 검사, 유효범위 경고, try 블록 중 어디에 먼저 손대는 것이 비용 대비 이득이 큰지, 8장 도구⑤의 경험을 근거로 논하라.
스스로 점검
- 지목 3문 중 1문만 설명에 실패하면 코드 이해 점수는 어떻게 되는가?
- 프롬프트 전략 8점의 채점 원본은 어느 기록인가? 학생 제출 로그는 어떤 용도인가?
15.3 2시간의 전략: 작동 우선, 증빙은 중간에
최종 시험은 AI 협업 사이클 여섯 블록 전체를 2시간 안에 한 바퀴 돌리는 시험이다. 매주 실습에서 블록 하나씩을 키웠다면, 오늘은 여섯 개를 이어 붙인다.
① 문제 정의 → ② 분해 → ③ 프롬프트 → ④ 생성 코드 읽기 → ⑤ 테스트 → ⑥ 화공 검증
15.3.1 시간 배분 모델
표 15-4는 120분의 기본 배분이다. 절대 규칙이 아니라 기준선이며, 기준선이 있어야 이탈을 감지할 수 있다.
| 구간 | 할 일 | 커밋 |
|---|---|---|
| 0:00–0:15 | 빈 첫 커밋 → 파라미터 파일 확인(단위·키·유효범위) → PLAN.md에 함수 단위 분해 | 빈 커밋 + 계획 커밋 |
| 0:15–1:00 | 코어 기능의 최소 작동 버전 | 0:30, 1:00 스냅샷 |
| 1:00–1:25 | 검증 루틴 3종 실행 + README 증빙 기록 | 검증 커밋 |
| 1:25–1:45 | 확장: 작도, 예외 처리, 부가 기능 | 1:30 스냅샷 |
| 1:45–2:00 | README 실행법 재검, 선언문 작성, 최종 push | 최종 커밋 (1:55 목표) |
이 시간표를 따르면 커밋은 저절로 7회 안팎이 된다. 규정 최소치 5회는 목표라기보다 부산물이다. 최종 push를 1:55에 두는 이유도 단순하다. 2:00 정각의 push는 네트워크 지연 하나로 동결 시한을 넘길 수 있다.
구간마다 논리가 있다. 계획 구간의 15분은 코드를 한 줄도 만들지 않는 시간이지만, PLAN.md의 함수 단위 분해가 그대로 프롬프트 전략 4점의 첫 증거가 된다. 코어 구간 45분의 원칙은 작동 우선이다. 뼈대를 먼저 세우고 실행 가능한 상태를 유지하며 조금씩 키운다. 프로그래밍 교재들이 점진적 개발(incremental development)이라 부르는 이 습관은 5장에서 도입했고, 시간 압박 아래에서 진가를 발휘한다. 한 번에 조금만 바꾸면 버그가 생겨도 방금 바꾼 곳만 의심하면 되기 때문이다.
검증 구간을 코어 직후, 확장 이전에 두는 배치가 이 모델의 핵심 설계다. 검증 증빙은 작동성 14점의 필수 항목이면서 프롬프트 전략의 검증 습관 2점과도 연결된다. 기능을 다 만든 뒤 검증하겠다는 계획은 15주 내내 본 실패 패턴이다. 확장이 늦어지면 증빙이 통째로 사라진다. 코어가 돌아가는 즉시 대조값 1점을 확보해 README에 적고 커밋하라. 그 순간부터 무슨 일이 생겨도 "검증된 코어"까지는 인정받는다.
15.3.2 첫 15분: 예제로 보는 분해
예제 15.3-1 기포점 계산기 주제의 첫 15분
문제 시험이 시작되었다. 주제는 "학번별 이성분계의 기포점 계산기와 T-xy 작도(NIST 대조 검증 포함)". 배부된 파라미터 파일에는 두 성분의 Antoine 상수(log₁₀ 기준, 압력 kPa·온도 K, 유효 온도범위 포함)와 전압 P가 들어 있다. 첫 15분의 계획을 세우고 코어 함수의 뼈대를 확보하라.
풀이 전략 문제를 네 블록으로 분해한다. ① antoine(T, A, B, C)(8장 도구⑤와 동형), ② bubble_T(기포점 온도 계산), ③ 조성을 바꿔 가며 ②를 반복하는 T-xy 루프, ④ 작도와 README. 코어는 ②다. 9장에서 세운 조건을 다시 쓰면, 액상 조성 x1에서 기포점 온도 T는 다음을 만족한다.
x1 p1*(T) + (1 − x1) p2*(T) = P (15.1)
코드 생성은 AI에게 맡기고, 파라미터 파일의 단위 확인과 검증, README는 직접 한다. 상수를 프롬프트에 적어 주지 않는다. 코드가 파일에서 읽게 해야 AI가 상수를 지어낼 여지가 없다.
이상용액의 기포점 온도를 이분법으로 구하는 함수 bubble_T를 만들어 줘. 인자는 x1, 전압 P(kPa), 두 성분의 Antoine 상수 튜플 ant1·ant2, 온도 구간 T_low·T_high(K). antoine(T, A, B, C) 함수는 이미 있어. 주석은 한국어로, 물리량 주석에 단위를 달아 줘.
def bubble_T(x1, P, ant1, ant2, T_low, T_high):
"""이분법으로 기포점 온도를 구한다. P는 kPa, T는 K."""
def f(T): # f(T) = x1*p1* + x2*p2* - P
p1 = antoine(T, *ant1) # 성분 1 증기압 [kPa]
p2 = antoine(T, *ant2) # 성분 2 증기압 [kPa]
return x1 * p1 + (1 - x1) * p2 - P
for _ in range(60): # 60회 반복이면 구간 폭이 충분히 줄어든다
T_mid = (T_low + T_high) / 2
if f(T_low) * f(T_mid) <= 0: # 부호가 바뀌는 쪽 절반을 남긴다
T_high = T_mid
else:
T_low = T_mid
return T_mid # 기포점 [K]
✔ 식 (15.1)과 잔차 정의가 일치하고 단위 주석이 요청대로 붙었다. x1 = 1을 넣으면 f(T) = p1*(T) − P가 되어 순수 성분 1의 비점을 돌려줘야 한다. 배부 데이터의 비점과 0.5 K 이내로 일치함을 확인. 다만 구간 안에 해가 없을 때의 처리가 없다. 확장 구간에서 부호 검사를 추가하기로 하고 PLAN.md에 기록.
- 단위 체크: T[K] 입력, p*[kPa], P[kPa] (파라미터 파일의 단위 표기와 일치)
- 대조값: x1 = 1의 반환값을 배부 데이터의 순수 성분 비점과 대조(README에 기록), NIST WebBook 1점 대조는 부록 B 절차
- 극한값: x1 = 0과 1에서 각 순수 성분 비점 회수, Antoine 유효범위 밖 온도에서 경고가 뜨는가
분석 15분의 산출물은 완성 코드가 아니라 계획 커밋과 돌아가는 뼈대다. 분해가 끝나 있으면 이후의 모든 프롬프트가 함수 하나 크기로 작아지고, 생성 코드를 읽는 부담도 그만큼 준다. 극한값 검증이 프롬프트 검증 코멘트에 이미 들어 있다는 점을 눈여겨보라. 검증은 별도 단계이기 전에 코드를 받아드는 순간의 반사 동작이다. 전압 P를 절반으로 바꿔 기포점이 내려가는지도 확인해 보라.
생각해보기 1시간 30분 시점에 코어 기능이 아직 돌아가지 않는다. 남은 30분 동안 할 일의 우선순위를 정하고, 표 15-2의 배점을 근거로 그 순서를 정당화하라. 기능 완성을 계속 미는 선택과 지금 상태를 검증·증빙하고 한계를 기록하는 선택은 각각 몇 점짜리 도박인가?
15.4 비상 프로토콜: 막힘과 장애
비상은 두 종류다. 내 코드가 막히는 것과 인프라가 무너지는 것이다. 대응 원칙이 다르므로 나눠 다룬다.
15.4.1 코드가 막혔을 때: 디버깅 triage
디버깅은 실험 과학처럼 진행한다. 증상을 보고 가설을 하나 세우고, 그 가설을 배제할 수 있는 테스트를 하나 실행한다. 가설 없이 코드를 이리저리 바꿔 보는 것을 무작위 수정(random walk programming)이라 하는데, 시간이 무한할 때조차 느린 방법이고, 시험장에서는 시간을 가장 많이 잃는 경로다.
에러 메시지가 있다면 절반은 온 것이다. 메시지 전문을 그대로 AI에 붙여넣고 원인 후보 3개와 각각의 확인 방법을 요구하라. 11주차 디버깅 실습에서 연습한 패턴이며, 이 대화 자체가 디버깅 프롬프트 2점의 증거로 로그에 남는다.
한 버그에는 타임박스(timebox)를 건다. 15분을 넘기면 두 갈래 중 하나를 고른다. 하나는 후퇴(retreating)다. 마지막으로 작동하던 스냅샷 커밋으로 되돌아가 거기서 다시 쌓는다. 30분 스냅샷 규정의 세 번째 기능이 여기 있다. 후퇴 지점이 항상 30분 이내 거리에 존재한다. 다른 하나는 우회다. 그 기능을 축소하거나 제외하고 README의 한계 항목에 기록한다. 한계·개선점 3점은 이때 오히려 확보된다.
말로 푸는 방법도 있다. 문제를 남에게(상대가 없으면 오리 인형에게라도) 소리 내어 설명하다가 스스로 답을 발견하는 현상은 고무 오리 디버깅(rubber duck debugging)이라는 이름이 붙을 만큼 흔하다. 시험장에서 오리의 자리는 AI가 대신한다. "지금 상황을 설명할 테니 내 가정 중 틀린 것을 찾아 줘"라고 쓰는 순간, 설명하는 행위 자체가 구술 평가 리허설이 된다.
15.4.2 인프라가 무너졌을 때
강의계획서에 명문화된 비상 프로토콜은 다섯 겹이다. 예비 API 제공자 1개가 게이트웨이에 상시 등록되어 있고, 시험은 유선랜 전산실습실에서 치르며, 예비 노트북 2~3대가 준비되고, 30분 스냅샷 커밋이 부분 채점의 근거가 되며, 전면 장애 시에는 업로드가 24시간 연장되고 발표만 당일 진행된다. 학생이 외울 것은 프로토콜의 존재보다 자기 행동 수칙이다.
장애가 의심되면 먼저 손을 들어 감독에게 알린다. 장애 시각이 기록되어야 구제의 기준점이 생긴다. 그리고 커밋을 멈추지 않는다. Git은 로컬 저장소이므로 네트워크가 죽어도 커밋은 계속 쌓을 수 있고, push만 복구 후에 몰아서 하면 이력의 시각은 그대로 보존된다. API가 죽어 AI를 못 쓰는 상황이라면, 그때부터는 시험1에서 검증한 개인 두뇌(코드 읽기와 의사코드)로 버티는 구간이다.
표 15-5는 비상 유형별 첫 행동을 요약한 것이다.
| 유형 | 1차 행동 | 점수를 지키는 장치 |
|---|---|---|
| 에러 발생 | 메시지 전문 붙여넣기 + 원인 후보 3개 요구 | 디버깅 프롬프트 2점 |
| 논리 오류(값이 이상함) | 가설 1개 + 배제 테스트 1개, 15분 타임박스 | 스냅샷 = 후퇴 지점 |
| 시간 부족 | 기능 축소 + README 한계 기록 | 한계·개선점 3점 |
| 네트워크·API 장애 | 감독에게 알림 + 로컬 커밋 지속 | 30분 스냅샷 = 부분 채점 |
스스로 점검
- 네트워크가 완전히 끊겼다. 커밋과 push 중 지금 가능한 것은 무엇이고, 그 이유는 무엇인가?
- 한 버그에 20분째 매달려 있다. 이 절의 기준으로 지금 할 일 두 가지 선택지를 말하라.
15.5 수업 이후의 학습 OS
종강하면 수업은 끝나지만 repo는 남는다. 도구 사다리 아홉 개, 15주치 위키, 그리고 게이트웨이 로그에 찍힌 수백 번의 프롬프트가 이 학기의 물증이다. 마지막 절은 그 물증을 자산으로 바꾸는 이야기다.
15.5.1 15주의 회고: 무엇이 몸에 남았는가
1주차에 역산했던 7대 역량을 되짚어 보자. 문제 분해(C1)와 생성 코드 읽기(C2)는 도구가 바뀌어도 그대로 쓰이는 근육이다. Git 규율(C3)은 어느 연구실, 어느 회사에서든 협업의 공용어다. 화공 문제의 계산 가능화(C4)는 3학년 전공에서 곧바로 이자가 붙는다. 검증 루틴 3종(C5)과 시간 압박 하 완주(C6)는 오늘 최종 시험이 측정한 것이고, 학습 OS 운영(C7)은 측정이 끝나도 계속 돌아간다.
이 책이 15주 내내 연습시킨 태도를 요약하는 문장을 마지막으로 적는다. 목표는 프로그램을 작동시키는 것만이 아니라 작동시키는 방법을 배우는 것이다(Downey, Think Python). 최종 시험의 40점은 그 방법이 몸에 남았는지를 측정하는 눈금이다.
15.5.2 위키와 도구의 유지·확장
위키의 성장단계 태그(#seed → #growing → #evergreen)는 학기와 함께 끝나지 않는다. 다음 학기 전공 노트를 같은 구조로 이어 가라. 정리기와 플래시카드 생성기가 이미 그 구조를 읽고 쓰도록 만들어져 있으므로, 새 과목의 노트를 넣는 순간 2~4주에 조립한 파이프라인이 다시 돌기 시작한다. 방학 중에는 주 1회 의미 커밋 정도의 낮은 리듬이면 충분하다. 리듬이 끊긴 위키는 몰아서 복구되지 않는다는 것을 마일스톤 채점이 15주 동안 보여 주었다.
도구 사다리는 3학년 과목의 부품 창고가 된다. 분리공정에서 McCabe-Thiele 작도를 만나면 도구⑤⑥이 그대로 하부 구조다. 서브프로젝트 주제 풀의 1번이 그 예고편이었다. 재사용의 손동작은 한 줄이다.
# 3학년 분리공정 과제 — 2학년 때 만든 도구⑤를 부품으로 재사용
from tools.antoine import antoine # 도구⑤ (8장), log10·kPa·K 규약
p_star = antoine(T, A, B, C) # T[K] 입력 → p*[kPa]
import 한 줄이 가볍게 보이지만, 이 한 줄이 성립하려면 함수에 단위 규약이 문서화되어 있고 검증 기록이 남아 있어야 한다. 15주 동안 README와 주석에 단위를 적게 한 규칙이 미래의 자신에게 지불한 보험료였던 셈이다.
15.5.3 검증 루틴은 코드 밖으로 나간다
단위 체크, 대조값, 극한값은 코드 검증법이기 이전에 공학 계산 일반의 검증법이다. 물질수지를 손으로 풀 때 나가는 유량이 들어오는 유량보다 크면 어딘가 틀린 것이고, 실험 데이터의 피팅 결과는 알려진 문헌값 한 점과 대조해야 하며, 극한 조건에서 말이 되는지는 보고서의 어느 수치에든 물을 수 있다. AI가 쓴 보고서 초안부터 논문 요약과 설계 계산까지, 앞으로 마주칠 모든 생성물에 같은 세 질문을 던지면 된다. 이 수업이 남기려 한 습관이 이것이다.
AI 도구는 바뀐다. 이 책이 다룬 모델과 게이트웨이도 몇 년 안에 다른 이름으로 대체될 것이다. 그러나 문제를 분해하고, 생성물을 읽고, 화학공학의 물리로 검증하는 사이클은 도구의 이름과 무관하게 남는다. 사이클을 가진 사람에게 새 도구는 부품 교체에 가깝다.
실습 15: 최종 시험 실기 (120분)
준비물
- 유선랜 전산실습실 좌석, 노트북과 전원 어댑터 (예비 노트북 2~3대는 감독석에 준비된다)
- 게이트웨이 가상 키 동작 확인 (시험 시작 전 응답 1회 수신)
- GitHub 로그인 상태 + 시험용 빈 repo 주소 수령
- 본인 위키 열람 가능 상태 (참조 허용, 활용 시 가점)
- 재사용할 이전 도구: 도구④~⑨ 전부 (본인 repo에서 복사·import 자유)
과제 (당일 절차)
- 실습 15.1 시험 시작과 동시에 빈 repo를 클론하고 빈 첫 커밋을 push하라. (완료 확인: 원격 이력에 커밋 1개, 시작 시각의 기준점)
- 실습 15.2 파라미터 파일을 열어 단위·키 이름·유효범위를 확인하고, PLAN.md에 함수 단위 분해를 적어 커밋하라. (0:15 이전)
- 실습 15.3 코어 기능의 최소 작동 버전을 만들라. 0:30과 1:00에 스냅샷 커밋을 남기라. (완료 확인: 대표 입력 1건에서 출력이 나온다)
- 실습 15.4 검증 루틴 3종을 실행하고 결과를 README에 기록한 뒤 커밋하라. (1:25 이전, 작동성 차원의 필수 항목)
- 실습 15.5 예외 처리·작도·부가 기능으로 확장하고 1:30 스냅샷 커밋을 남기라.
- 실습 15.6 README의 실행 방법을 처음 보는 사람의 눈으로 재검하고, AI 사용내역 선언문 1쪽을 추가한 뒤 1:55까지 최종 push하라. (종료 즉시 동결, 사후 수정분은 채점 제외)
완성 기준 (제출 요건)
- 커밋 5회 이상 (빈 첫 커밋 포함, 30분 간격 스냅샷 존재)
- README: 실행 방법 + 검증 3종 증빙(단위·대조값·극한값) + 한계·개선점
- AI 사용내역 선언문 1쪽 첨부 (미첨부는 부정행위 처리)
- 종료 시각 전 push 완료, 이후 수정 없음
- 구술 평가(시험주간): 3분 데모 + 지목 구술 2분. 본인 repo의 어느 줄이든 설명 가능한 상태로 입장
막혔는가?
- 분해가 안 될 때: "지금 만들려는 도구를 함수 단위로 분해해 줘. 각 함수의 입력·출력·단위를 표로." 받은 표를 그대로 붙여넣지 말고 스스로 검토해 PLAN.md에 옮겨 적어라.
- 에러가 났을 때: "다음 에러 메시지의 원인 후보 3개와 각각의 확인 방법을 알려 줘:" 뒤에 메시지 전문을 붙여라.
- 시간이 부족할 때: "이 코드에서 기능 X를 뺀 최소 버전으로 줄여 줘. 나머지 동작은 그대로." 제외한 기능은 README 한계 항목에 적어라.
기록 제출 동결 후 당일 안에 위키에 회고를 커밋하라: 오늘 만든 것, 막힌 것, 틀린 것 각 1건. 이 커밋은 위키 마일스톤③ 집계에 포함되며, 구술 평가 전에 다시 읽으면 자신에게 최적화된 예상 문제집이 된다.
요약
- S 15-1 커밋 규정: 빈 repo 첫 커밋 → 30분마다 스냅샷 → 2시간 내 5회 이상 → 종료 즉시 업로드·동결(사후 수정분 채점 제외).
- S 15-2 시간 배분: 15분 계획 / 45분 코어(작동 우선) / 25분 검증·증빙 / 20분 확장 / 15분 제출(1:55 push).
- S 15-3 비상 규칙: 에러 전문 붙여넣기 → 가설 1개·배제 테스트 1개 → 버그당 15분 타임박스 → 후퇴(스냅샷) 또는 축소(README 한계 기록).
- S 15-4 디버깅 프롬프트 패턴: "다음 에러 메시지의 원인 후보 3개와 각각의 확인 방법을 알려 줘: (전문)".
| 구분 | 시험1 | 시험2 | 시험3 |
|---|---|---|---|
| 배점 | 20 | 20 | 40 |
| 시기 | 7주 | 12주 | 15주 + 시험주간 |
| AI | 금지 | 필수 | 필수 |
| 형식 | 오프라인 지필 | 90분 현장 실기 | 2시간 바이브코딩 + GitHub + 구술 평가 |
| 커밋 규정 | — | 빈 repo 첫 커밋, 커밋 4회 이상 | 빈 repo 첫 커밋, 30분 스냅샷, 커밋 5회 이상, 즉시 동결 |
| 검증하는 것 | AI 없이 남는 개인 두뇌 | 실기 형식 리허설 | 사이클 6블록의 통합 수행 |
1장에서 선언한 AI 협업 사이클은 매주 블록 하나씩 근육이 붙어 오늘 여섯 블록을 2시간에 완주하는 데까지 왔다. 도구 사다리는 단위환산기에서 대시보드까지 아홉 칸을 다 올랐고, 그 아홉 개가 시험장에서 부품으로 재사용되었다. 이 장으로 책은 끝나지만 사이클은 멈출 이유가 없다. 다음 문제, 다음 과목, 다음 도구에서 같은 여섯 블록이 돈다.
용어 정리
- 커밋 포렌식 (commit forensics)
- 커밋 이력의 시간 구조로 작업 과정을 판독하는 채점 기법. 첫 커밋이 완성본인 repo는 자동 플래그 대상이다.
- 부분 채점 (partial credit)
- 장애 등으로 미완성인 제출물에서 완료가 입증된 부분까지 점수를 인정하는 채점. 30분 스냅샷 커밋이 그 근거가 된다.
- 무작위 수정 (random walk programming)
- 가설 없이 코드를 이리저리 바꿔 보며 우연한 성공을 기다리는 디버깅 안티패턴.
- 타임박스 (timebox)
- 한 문제에 쓸 시간의 상한을 미리 정해 두는 시간 관리 기법. 이 수업 기준은 버그당 15분이다.
- 후퇴 (retreating)
- 마지막으로 작동하던 버전으로 되돌아가 거기서 다시 쌓는 디버깅 전략. 스냅샷 커밋이 후퇴 지점을 제공한다.
- 고무 오리 디버깅 (rubber duck debugging)
- 문제를 소리 내어 설명하는 행위 자체로 원인을 발견하는 기법. 설명 상대는 사람, 인형, AI 무엇이든 된다.
연습문제
Q군: 개념·읽기 (AI 없이 풀 것)
- Q15.1 시험 종료 5분 뒤 버그 수정 커밋을 push했다. 이 커밋이 채점에서 어떻게 처리되는지, 표 15-1의 근거 조문을 들어 설명하라.
- Q15.2 어떤 제출물이 README대로 실행 5점 만점, 핵심 기능 6점 중 4점, 예외 처리 0점을 받았고, 구술 평가 지목 3문 중 2문 설명에 실패했으며, 프롬프트 전략 6점과 발표 6점을 받았다. 표 15-2의 루브릭으로 총점을 계산하라.
- Q15.3 코드 15-2의
for _ in range(60)이 하는 일을 한국어 두 문장으로 설명하고, 반복 횟수를 고정한 이 방식의 한계를 한 가지 제시하라.
P군: 제작·계산 (AI 사용 전제)
- P15.1 주제 예시 중 "학번별 실험 데이터 회귀와 이상치 자동 탐지 리포트 생성기"를 골라, 표 15-4의 시간표와 커밋 규정 전부를 적용한 2시간 셀프 모의시험을 수행하라. 종료 후 본인 커밋 이력을 코드 15-1의 기준으로 자가 판독하라.
- P15.2 비상 리허설: 네트워크를 끊은 상태에서 로컬 커밋을 3회 쌓은 뒤 재연결하여 push하라. 각 단계에서 사용한 Git 명령과
git log출력을 기록하고, 커밋 시각이 보존되는지 확인하라. - P15.3 (메타인지) 루브릭 4개 차원에서 각각 점수를 잃도록 제출물을 일부러 나쁘게 설계하는 방법을 차원별로 한 가지씩 제시하고, 각 방법이 몇 점을 잃게 하는지 계산하라. 그중 자신의 현재 습관과 가장 가까운 위험 하나를 고르고 대책을 적으라.
위키 기록 과제 ① 이 장의 핵심을 "최종 시험 당일 체크리스트" 1페이지로 정리하라(#seed, 구술 평가 뒤 #evergreen 승격 후보). ② 시험2 또는 모의 실기에서 내가 틀렸던 것 1건을 에러 화면과 함께 기록하라. ③ 검증 루틴 3종을 타 과목 계산 1건에 적용한 사례를 남겨라. 이번 주 의미 커밋 1회 이상이 마일스톤③ 채점 지표다.
부록 A: 셋업 가이드(Windows에서 수업 환경 만들기)
이 부록은 1주차 셋업 실습의 참고서이자, 학기 중 환경이 깨졌을 때 돌아올 복구 매뉴얼이다. 수업에 필요한 것은 세 가지다. Git(버전 관리 도구), Claude Code(AI 코딩 도구), 그리고 게이트웨이 가상 키(AI 사용 권한)다. 실습실에서는 배포 스크립트 하나가 셋을 한 번에 처리하지만, 각 단계에서 무엇이 일어나는지 알아 두면 문제가 생겼을 때 어디를 보아야 할지 판단할 수 있다.
A.1 터미널 열기
키보드의 Windows 키를 누르고 "터미널" 또는 "PowerShell"을 입력해 실행해 보자. 검은 창에 다음과 비슷한 글자가 보이면 준비가 끝난 것이다.
PS C:\Users\holim>
이렇게 명령을 글자로 입력해 컴퓨터를 조작하는 창을 터미널(terminal)이라 한다. 마우스로 아이콘을 클릭하는 대신 명령어를 한 줄씩 쳐서 프로그램을 실행하며, 줄 앞의 PS C:\...> 표시가 "다음 명령을 기다리는 중"이라는 신호다. Windows의 표준 터미널 환경이 PowerShell이며, 이 수업의 모든 터미널 작업은 PowerShell 또는 Windows Terminal에서 한다. Git 설치 시 딸려 오는 Git Bash라는 다른 터미널도 있는데, 거기서는 Claude Code를 실행하지 않는다. 이유는 A.3에서 다룬다.
A.2 Claude Code 설치: PowerShell 한 줄
터미널이 열렸으면 설치는 한 줄이다. 아래 명령을 그대로 입력하고 Enter를 누르라.
irm https://claude.ai/install.ps1 | iex
이 명령은 공식 설치 스크립트를 내려받아 곧바로 실행한다. Claude Code는 Windows에서 네이티브로 동작하므로 Node.js나 WSL 같은 사전 설치물은 필요 없다. 설치가 끝나면 지금 열려 있는 터미널 창을 닫고, 새 터미널을 열어 다음으로 확인하라.
claude --version # 버전 번호가 출력되면 설치 성공
버전 번호 대신 빨간 에러가 나오면 A.3의 함정 1에 해당할 가능성이 크다. Git for Windows는 별도로 설치해야 하는데(GitHub 제출이 수업의 채점 경로이므로 필수), 실습실 배포 스크립트(A.4)가 함께 처리한다.
A.3 흔한 함정 두 가지
A.3.1 함정 1: "claude를 찾을 수 없다"
설치 직후 같은 터미널 창에서 claude를 치면 다음 에러가 나온다.
claude : 'claude' 용어가 cmdlet, 함수, 스크립트 파일 또는 실행할 수 있는
프로그램 이름으로 인식되지 않습니다.
프로그램이 설치되지 않은 것이 아니다. 터미널은 명령어를 PATH라는 폴더 목록에서 찾는데, 설치가 바꾼 PATH는 이미 열려 있는 창에는 적용되지 않고 새로 여는 터미널부터 적용된다. 처방은 단순하다. 창을 닫고 새 터미널을 열면 된다. 재설치는 필요 없다.
A.3.2 함정 2: Git Bash에서 실행
Git Bash에서 claude를 실행하면 "Raw mode is not supported"류의 오류가 나며 대화 화면이 뜨지 않는다. Claude Code는 화면을 실시간으로 다시 그리는 대화형 인터페이스를 쓰는데, Git Bash의 터미널 계층이 이 방식을 지원하지 않기 때문이다. Claude Code는 PowerShell 또는 Windows Terminal에서만 실행한다. Git Bash는 지우지 않아도 된다. 쓰지 않으면 그만이다.
두 함정 모두에서 기억할 태도가 있다. 에러 메시지는 컴퓨터를 망가뜨리지 않는다. 빨간 글자가 나오면 당황해서 창을 닫지 말고, 메시지 전문을 복사해 두라. 그 문자열 그대로 검색하거나 질문에 붙여넣는 것이 가장 빠른 해결 경로이며, 이 수업의 디버깅 프롬프트 패턴(부록 C)도 전부 "에러 전문 붙여넣기"에서 출발한다.
A.4 배포 스크립트로 한 번에 끝내기
1주차 실습실에서는 A.2를 직접 칠 필요 없이 수업 배포 스크립트 한 개로 셋업을 끝낸다. 스크립트가 하는 일은 세 가지다. Git for Windows 설치, Claude Code 설치, 그리고 설정 파일(settings.json)에 게이트웨이 주소와 본인의 가상 키 기록이다. 전체 소요는 약 10분이다.
- LMS 공지에서 배포 스크립트(
setup.ps1)를 내려받아라. (다운로드 폴더에 파일이 보인다) - PowerShell을 열고 다운로드 폴더로 이동하라:
cd ~\Downloads(프롬프트의 경로 표시가 바뀐다) - 스크립트를 실행하라:
.\setup.ps1(진행 메시지가 순서대로 출력된다) - 실행이 끝나면 창을 닫고 새 터미널에서
git --version과claude --version을 확인하라. (둘 다 버전 번호가 나온다)
3단계에서 보안 설정이 스크립트 실행을 차단하는 기기가 있다. 그 경우 다음 한 줄로 실행한다.
powershell -ExecutionPolicy Bypass -File .\setup.ps1
이 방법으로도 진행되지 않으면 붙잡고 있지 말고 손을 들어 조교를 부르라. 1주차 실습에는 셋업 실패를 전제로 현장 지원이 배치되어 있으며, 그래도 안 되는 기기를 위한 최종 경로가 A.6의 Codespaces다.
A.5 게이트웨이와 가상 키
이 수업의 AI 호출은 각자 개인 계정이 아니라 교수가 운영하는 게이트웨이(gateway) 서버를 거친다. Claude Code의 설정에서 접속 주소(ANTHROPIC_BASE_URL)가 게이트웨이를 가리키고, 학생에게는 원 제공자의 키 대신 학생별 가상 키(virtual key)가 발급된다. 이 구조가 존재하는 이유는 세 가지다.
첫째는 비용이다. 가상 키에는 1인당 주간 토큰 상한이 걸려 있고, 초과하면 저가 모델로 자동 전환된다. 한 명의 폭주가 수업 전체의 예산을 소진하는 일을 막는 장치다. 둘째는 채점이다. 게이트웨이의 서버측 세션 로그는 최종 시험 루브릭 중 프롬프트 전략 8점의 증거 원본이 된다. 셋째는 안정성이다. 시험 당일 제공자 장애가 나면 게이트웨이에 상시 등록된 예비 제공자로 전환한다.
가상 키는 학생증과 같은 개인 자격 증명이다. 다른 사람과 공유하지 않고, 코드나 저장소에 적지 않는다(키를 지키는 구체적 방법은 부록 D.3에서 다룬다). 유출이 의심되면 즉시 교수나 조교에게 알리라. 가상 키는 회수와 재발급이 되므로, 알리는 순간 사고는 수습된다. 키 설정은 배포 스크립트가 settings.json에 기록하므로 직접 만질 일이 없으며, 설정이 꼬였을 때의 처방도 스크립트 재실행이다.
A.6 백업 경로: GitHub Codespaces
노트북이 없거나, 사양이 낮거나, 위의 모든 절차가 실패한 경우를 위한 경로가 GitHub Codespaces다. Codespaces는 브라우저 안에서 열리는 클라우드 개발 환경으로, 로컬 설치 없이 터미널과 편집기를 쓸 수 있다. GitHub Student Developer Pack을 신청하면 학생에게는 월 180 core-hours가 제공되어 이 수업 분량(주 2시간 실습 + 과제)은 사실상 무료로 감당된다.
준비는 두 가지다. GitHub 계정으로 Student Developer Pack을 신청해 두는 것, 그리고 1주차에 발급받는 수업 저장소에서 Codespace를 한 번 열어 보는 것이다. Codespaces 환경에서의 세부 설정 절차는 LMS 게시물과 실습 시간의 안내를 따르라. 셋업이 실패해도 1주차 실습을 완주할 길은 남아 있다.
A.7 셋업 완료 기준
- 새 터미널에서
git --version이 버전 번호를 출력한다. - 새 터미널에서
claude --version이 버전 번호를 출력한다. claude를 실행해 첫 질문을 보내고 응답을 받는다. (게이트웨이·가상 키가 살아 있다는 증거)- GitHub 계정으로 로그인되고, 1주차에 발급받은 수업 저장소 페이지가 브라우저에서 열린다.
막혔는가?
- "다음 에러 메시지가 나왔다. [에러 전문 붙여넣기] 원인 후보 3개와 각각의 확인 방법을 알려 줘."
- "claude --version은 되는데 claude 실행 시 연결 오류가 난다. 설정 파일에서 확인할 항목을 순서대로 알려 줘."
- 위 프롬프트로도 15분 안에 풀리지 않으면 조교 호출이 가장 빠르다. 셋업은 실력 평가 대상이 아니다.
네 항목이 모두 통과되면 환경은 완성이다. 이제 1장의 첫 바이브코딩 15분으로 돌아가자.
부록 D: Git·GitHub 최소 생존 가이드
이 수업에서 Git은 선택 기술이라기보다 제출 통로에 가깝다. 시험2와 최종 시험은 GitHub 업로드가 곧 제출이고, 과제의 위키 6점은 커밋 이력이 채점 지표이며, 매주 실습의 완성 기준에도 커밋 수가 들어 있다. 반대로 말하면, 여기 담긴 최소한만 몸에 붙이면 15주의 어떤 평가에서도 제출 절차 때문에 점수를 잃을 일은 거의 없다. 명령어는 여섯 개면 충분하다.
D.1 세 가지 개념
파일의 변경 이력을 기록하고 필요하면 과거 시점으로 복원하는 체계를 버전 관리(version control)라 한다. 버전 관리되는 프로젝트 폴더와 그 이력 전체를 저장소(repository)라 하고, 의미 있는 변경 한 덩어리를 이력에 기록하는 행위를 커밋(commit)이라 한다. GitHub는 저장소를 인터넷에 보관·공유하는 서비스이며, 그곳에 있는 사본을 원격 저장소(remote repository)라 한다.
커밋은 파일 저장(Ctrl+S)과 다르다. 저장은 마지막 상태 하나만 남기지만, 커밋은 시점마다 스냅샷과 메시지를 남겨 "언제, 무엇을, 왜 바꿨는가"의 타임라인을 만든다. 시험2의 과정 점수가 커밋 타임라인을 근거로 매겨지는 이유가 여기 있다. 결과물만으로는 보이지 않는 작업 과정이 커밋 열에는 보인다.
D.2 커밋 규약: 기능 단위, 그리고 메시지
이 수업의 커밋 규약은 두 줄이다. 첫째, 커밋은 기능 단위로 한다. "환산 함수 하나가 동작하게 됐다", "검증 테스트를 통과했다", "에러를 고쳤다"처럼 한 문장으로 설명되는 작업 한 덩어리가 커밋 하나다. 2시간 작업을 마지막에 커밋 한 개로 몰아넣으면 과정이 사라지고, 반대로 파일을 저장할 때마다 기계적으로 커밋하면 타임라인이 소음으로 덮인다.
둘째, 메시지는 무엇을 했는지가 드러나는 한 줄 요약으로 쓴다. 몇 주 뒤의 자신과 채점자가 커밋 목록만 읽고 작업 흐름을 재구성할 수 있어야 한다.
| 판정 | 커밋 메시지 | 이유 |
|---|---|---|
| 좋음 | Antoine 함수 추가: 유효범위 밖 온도에 경고 발생 | 무엇이 생겼고 어떤 동작을 하는지 한 줄에 담김 |
| 좋음 | 단위 버그 수정: kPa를 bar로 잘못 반환하던 것 교정 | 고친 내용과 원인이 남음(오답노트를 겸한다) |
| 나쁨 | 수정 | 무엇을 수정했는지 아무 정보가 없음 |
| 나쁨 | 최종, 진짜최종, ㅁㄴㅇㄹ | 타임라인 전체를 소음으로 만든다 |
커밋의 기본 호흡은 세 명령이다.
git add -A # 바뀐 파일 전부를 커밋 후보에 올린다
git commit -m "Antoine 함수 추가: 유효범위 검사 포함"
git push # 원격 저장소(GitHub)에 업로드한다
실습의 각 단계가 끝날 때마다 이 세 줄을 한 호흡으로 반복하라. 이 습관이 몸에 붙어 있으면 시험 규정(D.4)의 스냅샷 커밋은 따로 연습할 필요가 없는 동작이 된다.
D.3 .gitignore와 .env: 올리면 안 되는 것
저장소에는 올라가면 안 되는 파일이 있다. 대표가 가상 키 같은 비밀값이다. 관례상 비밀값은 .env라는 텍스트 파일에 모아 두고, 그 파일 자체를 Git이 무시하도록 저장소 최상위의 .gitignore 파일에 등록한다.
.env # 키·비밀값 — 절대 커밋하지 않는다
__pycache__/ # 파이썬이 만드는 캐시 폴더
*.pyc # 컴파일 부산물
.gitignore에 적힌 패턴과 일치하는 파일은 git add -A를 실행해도 커밋 후보에 오르지 않는다. 따라서 규율은 한 줄로 요약된다. 키 문자열은 .py 파일과 저장소 어디에도 직접 쓰지 않고, .env나 환경 변수처럼 .gitignore 뒤에 있는 곳에만 둔다.
D.4 시험 커밋 규정
시험2와 최종 시험의 커밋 규정은 다음과 같다. 시험2는 최종 시험과 같은 인프라·규정으로 치르는 4분의 3 스케일 리허설이다.
| 항목 | 시험2 (90분) | 시험3 (2시간) |
|---|---|---|
| 시작 | 빈 repo 첫 커밋 | 빈 repo 첫 커밋 의무 |
| 진행 | 커밋 규정 동일 적용 | 30분마다 스냅샷 커밋 |
| 최소 커밋 | 90분 내 커밋 4회 이상 | 2시간 내 커밋 5회 이상 |
| 종료 | 즉시 업로드 | 즉시 업로드 → 결과물 동결, 사후 수정분은 채점 제외 |
각 규정에는 이유가 있다. 빈 저장소의 첫 커밋은 시작 시각의 증거다. 첫 커밋이 이미 완성본이면 다른 곳에서 만들어 온 것으로 자동 플래그된다. 커밋 이력은 부정행위 판별의 포렌식 자료이기도 하다.
30분 스냅샷은 보험이다. 시험 중 정전이나 네트워크 장애가 나면 마지막 스냅샷까지가 부분 채점의 근거가 된다. 그래서 스냅샷은 완성된 상태일 필요가 없다. 미완성 코드라도 30분마다 커밋하라. 미완성 커밋은 감점 요소가 아니라, 장애가 났을 때 점수를 지켜 주는 증거가 된다.
동결 규정은 채점 대상을 확정한다. 종료 시각에 업로드된 상태가 채점되며, 그 이후의 수정 커밋은 채점에서 제외된다. 종료 5분 전에는 새 기능에 손대지 말고 마지막 push와 웹 화면 확인에 시간을 쓰는 것이 규정에 맞는 전략이다.
D.5 GitHub 업로드 절차
수업 저장소는 1주차에 GitHub Classroom으로 발급된다. 과제 링크를 누르면 본인 계정 아래에 저장소가 만들어지고 원격 연결까지 끝난 상태이므로, 학생이 할 일은 클론 이후의 반복 사이클뿐이다.
- 과제 링크를 클릭해 본인 저장소를 발급받아라. (브라우저 주소창에 본인 아이디가 붙은 저장소 주소가 생긴다)
- 터미널에서 저장소를 내려받아라:
git clone <저장소 주소>(같은 이름의 폴더가 생긴다) - 폴더로 이동해 작업하고, 단계마다 add–commit–push 호흡(코드 D-1)을 반복하라. (
git log --oneline에 커밋이 한 줄씩 쌓인다) - 첫 push에서 로그인 창이 뜨면 브라우저로 GitHub에 로그인하라. (한 번 인증하면 이후 push에서는 다시 묻지 않는다)
- 브라우저에서 저장소 페이지를 새로고침해 방금 커밋이 보이는지 확인하라. (커밋 메시지와 시각이 목록에 나타난다)
5단계가 제출 확인의 기준이다. 업로드 성공 여부는 내 터미널이 아니라 GitHub 웹 화면으로 판정한다. 시험장에서도 같다. push 명령이 끝났다고 자리를 정리하지 말고, 웹에서 마지막 커밋을 눈으로 확인한 뒤가 진짜 종료다.
학기 내내 쓰는 명령은 아래 여섯 개다.
| 명령 | 하는 일 | 쓰는 순간 |
|---|---|---|
git clone <주소> | 원격 저장소를 내 컴퓨터로 복제 | 과제 시작 시 1회 |
git status | 바뀐 파일·커밋 대기 상태 표시 | 헷갈릴 때 언제든 |
git add -A | 변경 전부를 커밋 후보로 등록 | 커밋 직전 |
git commit -m "..." | 스냅샷 + 메시지 기록 | 기능 단위 완료 시 |
git push | 커밋을 GitHub에 업로드 | 커밋 후, 시험 종료 전 필수 |
git log --oneline | 커밋 타임라인 확인 | 규정 커밋 수 점검 시 |
막혔는가?
- "git push에서 다음 에러가 나왔다. [에러 전문 붙여넣기] 원인 후보 3개와 각각의 확인 명령을 알려 줘."
- "git status 출력이 다음과 같다. [출력 붙여넣기] 커밋에 포함되지 않은 파일이 있는가?"
- "커밋을 했는데 GitHub 웹 화면에 안 보인다. 로컬 커밋과 원격 반영의 차이를 점검하는 순서를 알려 줘."
D.6 시험장 최종 점검
- 시작 직후: 빈 저장소에 첫 커밋을 남겼다.
- 진행 중: 30분 간격 스냅샷 커밋을 지켰다. (타이머·알람을 미리 설정하라)
- 커밋 수: 규정 최소 횟수(시험2는 4회, 최종 시험은 5회) 이상이다.
git log --oneline으로 센다. - README: 검증 3종(단위 체크·대조값·극한값)의 기록이 들어 있다. 최종 시험 작동성 14점의 필수 항목이다.
- 선언문: AI 사용내역 선언문 1쪽이 포함되어 있다. 미첨부는 부정행위로 처리된다.
- 종료 전: 마지막 push를 마치고, GitHub 웹 화면에서 마지막 커밋을 눈으로 확인했다.
이 목록은 시험만을 위한 것이 아니다. 5주차부터 매주 실습이 같은 형식으로 돌아가므로, 학기 중반이면 이 점검은 종이 없이도 도는 습관이 된다. 그것이 매주 실습을 시험의 축소판으로 설계한 이유다.
부록 B: 검증 루틴 필드 매뉴얼
이 부록은 5주차부터 15주 시험까지 모든 화공 도구에 반복 적용하는 검증 루틴 3종의 현장 매뉴얼이다. 본문 예제가 검증을 서사로 보여 주었다면, 여기서는 그 절차를 인쇄해 책상에 붙여 두고 쓸 수 있는 체크리스트, 대조값을 구하는 도구 사용법, 그리고 상수 하나를 처음부터 끝까지 검증하는 예제로 정리한다. 도구가 바뀌어도 루틴은 바뀌지 않는다. 그 불변성을 몸에 붙이는 것이 이 매뉴얼의 목적이다.
B.1 세 루틴 한눈에
검증은 세 축으로 나뉜다. 순서에는 이유가 있다. 단위가 어긋난 값은 대조할 필요도 없고, 대조값과 맞아도 유효범위 밖이면 우연일 뿐이기 때문이다.
| 루틴 | 한 문장 정의 | 대표 질문 |
|---|---|---|
| ① 단위 체크 | 입력·출력·상수의 단위와 식의 차원이 서로 맞는가 | "이 로그 밑과 압력 단위가 상수 표와 같은가?" |
| ② 대조값 | 알려진 값 1점과 계산값을 비교해 오차를 수치로 남긴다 | "NIST 끓는점에서 계산이 몇 % 벗어나는가?" |
| ③ 극한값 sanity | 유효범위·물리적 극한에서 결과가 말이 되는가 | "온도를 올리면 증기압이 커지는 방향이 맞는가?" |
B.2 인쇄용 체크리스트 3종
아래 세 표는 그대로 인쇄해 실습과 시험에서 한 칸씩 채우며 쓰도록 설계했다. □에 표시가 다 차기 전에는 "검증 완료"라 부르지 않는다.
B.2.1 체크리스트 ①: 단위 체크
| □ | 확인 항목 | 통과 조건 |
|---|---|---|
| □ | 로그 밑 | 식이 log₁₀인지 ln인지 확인(상수 출처 표기와 일치) |
| □ | 압력 단위 | bar·mmHg·kPa 중 무엇인지 확인(반환 단위와 일치) |
| □ | 온도 단위 | K인지 °C인지 확인(함수 입력과 일치) |
| □ | 차원 일관성 | 식 양변의 차원이 같은가 (예: 압력 = 질량/(길이·시간²)) |
| □ | 매직 넘버 | 상수마다 단위·출처가 주석으로 남아 있는가 |
B.2.2 체크리스트 ②: NIST·문헌 대조값
| □ | 확인 항목 | 통과 조건 |
|---|---|---|
| □ | 대조점 확보 | NIST·교과서의 알려진 값 1점 이상을 조건과 함께 확보 |
| □ | 출처 명기 | 대조값의 출처·측정 조건을 기록 |
| □ | 오차 계산 | (계산 − 실측)/실측을 % 로 계산 |
| □ | 허용선 판정 | 사전에 정한 허용 오차(예: ±5%) 이내면 통과, 초과면 원인 조사 |
| □ | 기록 | 대조값·출처·오차를 README에 남겼는가 |
B.2.3 체크리스트 ③: 극한값 sanity
| □ | 확인 항목 | 통과 조건 |
|---|---|---|
| □ | 유효범위 | 입력이 상수·상관식의 범위 안인가 (밖이면 외삽 경고) |
| □ | 물리적 극한 | T→0, x→1 같은 극한에서 결과가 말이 되는가 |
| □ | 부호·단조성 | 온도↑ → 증기압↑ 처럼 변화 방향이 맞는가 |
| □ | 0·음수 방어 | 분모 0, 음수 입력에서 예외가 나는가 |
| □ | 자릿수 감각 | 결과의 크기(자릿수)가 상식과 맞는가 |
B.3 NIST Chemistry WebBook에서 대조값 구하기
대조값 루틴의 1차 출처는 NIST Chemistry WebBook이다. Antoine 상수와 상변화 데이터를 무료로 조회할 수 있고, 각 표의 머리에 로그 밑·단위·유효범위가 명기되어 있어 단위 체크의 근거로도 쓰인다.
- 브라우저에서
webbook.nist.gov/chemistry에 접속하라. (검색 옵션 페이지가 열린다) - 이름 또는 화학식으로 물질을 검색하라. (예:
water또는H2O) - 물질 홈페이지에서 "Other data available" 항목의 "Phase change data"를 선택하라. (상변화 데이터 페이지로 이동한다)
- "Antoine Equation Parameters" 표에서 머리글의 식과 단위를 먼저 확인하라. (물의 경우
log10(P) = A − B/(T+C), P 단위 bar, T 단위 K로 명기되어 있다) - 사용할 온도가 포함된 유효 온도범위 행의 A·B·C를 고르라. 여러 행이 있으면 온도가 든 범위의 행을 쓴다. (범위 밖 행을 쓰면 외삽이 된다)
4단계의 머리글 확인을 건너뛰면 안 된다. 같은 물질이라도 출처에 따라 로그 밑(log₁₀/ln)과 압력 단위가 달라, 표기를 안 보고 상수만 옮기면 단위 체크에서 걸릴 오류를 코드에 그대로 심게 된다.
B.4 CoolProp으로 대조값 자동 조회하기
표를 손으로 찾는 대신, 파이썬 라이브러리 CoolProp으로 포화 증기압 같은 대조값을 코드 안에서 바로 얻을 수 있다. CoolProp은 표준 라이브러리가 아니므로 처음 한 번은 터미널에서 pip install CoolProp으로 설치해야 한다. Antoine 상관식이 몇 개의 계수로 근사한 값이라면, CoolProp의 PropsSI는 정밀 상태방정식으로 계산한 값이라 서로 독립적인 기준선이 된다. 두 값이 어긋나면 그 차이가 상관식의 오차다.
from CoolProp.CoolProp import PropsSI
# 물의 포화 증기압: 온도 T[K]에서 품질 Q=0(포화액) 기준, 반환 단위 Pa
p_sat_pa = PropsSI("P", "T", 373.15, "Q", 0, "Water")
print(p_sat_pa / 1e5, "bar") # 약 1.014 bar (101.4 kPa)
PropsSI의 반환 단위는 항상 SI(압력 Pa, 온도 K)다. 따라서 CoolProp을 대조값으로 쓸 때도 단위 체크가 먼저다. 반환값 Pa를 내 함수의 출력 단위(bar)로 맞춘 뒤에야 두 값을 비교할 수 있다. 373.15 K에서 약 1.014 bar가 나오는데, 이는 물이 1 atm 부근에서 끓는다는 상식과 일치하는 기준선이다.
B.5 예제: Antoine 상수 세트 필드 검증
루틴 3종을 한 상수 세트에 처음부터 끝까지 적용해 본다. 검증은 "날조된 상수"만 잡는 것이 아니라, "올바른 상수를 잘못 쓴 경우"도 잡는다. 최종 시험 루브릭의 작동성 14점은 이 세 줄(단위·대조값·극한값)을 README에 남겼는지를 필수 항목으로 채점하므로, 아래 예제의 verify-box 세 줄이 채점 답안의 형식이다.
예제 B.1 물의 Antoine 상수 검증
문제 NIST에서 얻은 물의 Antoine 상수 A = 3.55959, B = 643.748, C = −198.043(유효범위 379–573 K, log₁₀·bar·K, Liu and Lindsay 1970)을 373 K와 473 K에 적용하고, 세 루틴으로 결과를 검증하라.
풀이 전략 계산은 상관식 함수에 맡기고(코드 8-1의 antoine_p_bar 재사용), 사람은 세 루틴을 순서대로 돌린다. 대조값은 NIST 끓는점(373.17 K에서 1.013 bar)과 CoolProp을, 범위 판정은 상수 표의 유효범위를 쓴다.
A, B, C = 3.55959, 643.748, -198.043 # log10, bar, K / 유효범위 379-573 K
p_373 = antoine_p_bar(373.0, A, B, C) # bar
p_473 = antoine_p_bar(473.0, A, B, C) # bar
print(f"373 K: {p_373:.3f} bar / 473 K: {p_473:.2f} bar")
실행 결과는 다음과 같다.
373 K: 0.759 bar / 473 K: 16.53 bar
- ✔ 단위 체크: 식은 log₁₀, 반환 단위 bar, 입력 T [K]. NIST 표 머리글과 일치한다. C는 T에 더해지므로 K 단위를 가진다.
- ✔ 대조값: NIST 끓는점 373.17 K에서 p* = 1.013 bar여야 한다. 373 K 계산값 0.759 bar는 25% 낮다. 473 K 계산값 16.53 bar는 실측 15.55 bar와 6.3% 차이가 난다.
- ✔ 극한값 sanity: 373 K는 유효범위(379–573 K)의 아래쪽 밖이다(외삽). 473 K는 범위 안. 온도↑에 증기압↑ 방향은 맞다.
분석 상수 자체는 올바르다(단위 체크 통과). 그런데도 373 K에서 25% 오차가 난 이유는 상수가 틀려서가 아니라 유효범위 밖에서 썼기 때문이다. 대조값 루틴이 오차를 드러내고, 극한값 루틴이 그 원인(외삽)을 지목했다. 두 루틴을 함께 돌려야 "상수가 날조됐다"와 "올바른 상수를 잘못 썼다"를 구분할 수 있다. 373 K가 필요하면 344–373 K 행 상수(A = 5.08354)로 바꿔 다시 검증해 보라. 그 결과는 8장 예제 8.2-1이 이미 보여 준다.
부록 C: 프롬프트 패턴집
이 부록은 매주 실습에서 쓰는 프롬프트 12종을 좋은 예·나쁜 예 쌍으로 모은 것이다. 나쁜 프롬프트는 검증할 수 없는 덩어리를 부르고, 좋은 프롬프트는 읽고 테스트하고 대조할 수 있는 조각을 부른다. 두 프롬프트를 나란히 보는 것 자체가 이 부록의 교육 장치다. 모든 프롬프트는 한국어 그대로 싣는다.
패턴들은 AI 협업 사이클(① 문제 정의 → ② 분해 → ③ 프롬프트 → ④ 코드 읽기 → ⑤ 테스트 → ⑥ 검증)의 각 단계에 대응한다. 아래에서 각 패턴이 어느 블록을 훈련하는지 표시한다.
C.1 문제 분해: 큰 요청을 함수 하나로
나쁜 프롬프트 증기압 계산기 프로그램 하나 만들어 줘.
좋은 프롬프트 증기압 계산기를 세 부분으로 나눠 만들 거야. 먼저 Antoine 상수 A·B·C와 온도 T[K]를 받아 증기압을 bar로 반환하는 함수 하나만 만들어 줘. 입출력 단위는 docstring에 적어 줘.
전체를 한 번에 시키면 읽을 수도 테스트할 수도 없는 덩어리가 온다. 함수 하나 단위로 쪼개면 각 조각을 검증하며 쌓을 수 있다. (사이클 ②)
C.2 스타터 요청: 명세를 담은 첫 프롬프트
나쁜 프롬프트 파이썬 코드 짜 줘.
좋은 프롬프트 다음 명세로 함수 뼈대를 만들어 줘. 이름 antoine_p_bar, 입력 T[K]·A·B·C, 출력 p*[bar], 식은 log10(p*)=A−B/(T+C). 주석은 한국어로. 예외 처리는 아직 넣지 말고 핵심만.
이름·입출력·단위·식을 담은 스타터라야 재현 가능한 첫 코드가 나온다. "코드 짜 줘"는 매번 다른 뼈대를 만들어 내므로 비교 자체가 불가능하다. (사이클 ③)
C.3 코드 설명 요청: EiPE 방향
나쁜 프롬프트 이 코드 설명해 줘.
좋은 프롬프트 이 함수가 무엇을 입력받아 무엇을 반환하는지, 각 줄이 하는 일을 한국어 세 문장으로 요약해 줘. 그다음 이 코드가 처리하지 못하는 입력을 한 가지 짚어 줘.
"설명해 줘"는 장황한 재서술을 부른다. 입출력과 한계까지 물으면 내가 코드를 이해했는지 대조할 답이 온다. 시험1의 EiPE 문항과 같은 골격이다. (사이클 ④)
C.4 테스트 생성: 무엇을 검증할지 지정
나쁜 프롬프트 테스트도 만들어 줘.
좋은 프롬프트 antoine_p_bar의 pytest 테스트를 세 개 써 줘. (1) 물 373 K에서 결과가 1.0 bar에 ±5% 드는지 (2) 유효범위 밖 온도에 경고가 나는지 (3) A를 문자열로 넣으면 오류가 나는지.
무엇을 검증할지 내가 지정해야 테스트가 검증 도구가 된다. 그냥 맡기면 자기 코드를 통과시키기만 하는 테스트가 온다. (사이클 ⑤)
C.5 디버깅: 에러 전문 붙여넣기
나쁜 프롬프트 왜 안 돼?
좋은 프롬프트 다음 에러 전문을 그대로 붙인다. [ValueError 전문] 이 에러가 나는 원인 후보를 세 개, 각각 확인하는 방법과 함께 알려 줘. 코드는 아직 고치지 말고 원인부터.
에러 전문 없이 물으면 추측만 온다. "전문 + 원인 후보 3개 + 확인 방법"은 부록 A·D의 힌트 프롬프트와 같은 형식이다. (사이클 ⑤)
C.6 리팩터: 기준을 준 정리
나쁜 프롬프트 코드 좀 깔끔하게 해 줘.
좋은 프롬프트 이 함수를 동작은 그대로 두고, 상수 조회 부분과 계산 부분을 두 함수로 분리해 줘. 변수명은 물리량이 드러나게(p_sat_bar 등) 바꾸고, 매직 넘버는 이름 있는 상수로 빼 줘.
"깔끔하게"는 기준이 없어 동작까지 바뀔 수 있다. 분리 축·명명 규칙·매직 넘버 같은 구체 기준을 줘야 검증 가능한 리팩터가 된다. (사이클 ④)
C.7 검증 요청: 세 축을 코드에 심기
나쁜 프롬프트 이 값 맞아?
좋은 프롬프트 이 증기압 함수에 검증을 붙여 줘. (1) 입출력 단위 주석 (2) 물 373 K에서 NIST 끓는점(1.013 bar)과 비교하는 assert (3) 온도가 유효범위 밖이면 경고 출력. 대조값 출처도 주석에 적어 줘.
"맞아?"는 AI에게 자기 채점을 시키는 것이다. 단위·대조값·극한값 세 축을 코드에 심어야 사람이 판정할 근거가 남는다. (사이클 ⑥)
C.8 물성 확인: 날조 방지
나쁜 프롬프트 물의 Antoine 상수 알려 줘.
좋은 프롬프트 물의 Antoine 상수를 알려 주되, 로그 밑(log10/ln)·압력 단위·온도 단위·유효 온도범위·출처를 반드시 함께 적어 줘. 이 다섯이 없는 값은 쓰지 않을 거야.
상수만 물으면 밑·단위·범위가 빠진 값이나 날조가 온다. 다섯 항목을 강제하면 NIST 표와 대조할 수 있고, 못 채우는 값은 걸러진다. (사이클 ⑥)
C.9 예외 처리 요청: 입력별 반응 지정
나쁜 프롬프트 에러 안 나게 해 줘.
좋은 프롬프트 T가 유효범위(379~573 K) 밖이면 계산은 하되 '외삽: 오차 큼' 경고를 함께 반환하게 해 줘. T가 음수면 ValueError를 던지게 해 줘.
"에러 안 나게"는 오류를 삼켜 버려 위험하다. 어떤 입력에 어떤 반응(경고/예외)을 할지 지정해야 안전한 코드가 된다. (사이클 ⑤)
C.10 반복 개선: 결함 지적 후 재요청
나쁜 프롬프트 (새 창을 열어) 다시 만들어 줘.
좋은 프롬프트 방금 함수는 373 K에서 0.76 bar를 반환하는데 NIST 실측은 1.013 bar라 25% 낮다. 원인이 유효범위 밖 외삽인 것 같다. 범위 검사를 추가하고, 373 K에는 344~373 K 행 상수를 쓰도록 고쳐 줘.
처음부터 완성 코드를 기대하지 않는다. "무엇이 얼마나 틀렸다 + 원인 가설"을 담은 후속 프롬프트가 개선의 엔진이다. 같은 맥락을 이어야 AI가 앞 코드를 안다. (사이클 ⑥→③)
C.11 주석·문서화 요청: 단위와 범위를 남기기
나쁜 프롬프트 주석 달아 줘.
좋은 프롬프트 각 함수에 한국어 docstring을 달아 줘. 입출력의 물리량과 단위, 식의 로그 밑, 상수 유효범위를 반드시 포함해 줘. 물리량 변수 옆에는 단위를 한 줄 주석으로(T = 373.15 # K) 적어 줘.
그냥 "주석"이면 코드를 그대로 옮긴 잡음이 붙는다. 단위·범위 문서화를 지정하면 주석이 검증 자료가 되어 대조값 루틴을 돕는다. (사이클 ④)
C.12 시각화 요청: 축·단위·범위 명시
나쁜 프롬프트 그래프 그려 줘.
좋은 프롬프트 300~500 K에서 물의 증기압 곡선을 matplotlib으로 그려 줘. x축 '온도 [K]', y축 '증기압 [bar]'로 라벨을 달고, 유효범위(379~573 K) 밖 구간은 점선으로 구분해 줘.
축 라벨·단위·범위 표시가 없는 그래프는 검증할 수 없다. 무엇을 어떤 축·단위로 그릴지까지 지정해야 대조 가능한 그림이 돌아온다. (사이클 ⑥)
C.13 패턴 요약
열두 패턴을 관통하는 규칙은 하나다. 검증할 수 있게 요청하라는 것이다. 좋은 프롬프트는 대체로 이름·입출력·단위·범위·판정 기준 중 몇 가지를 명시했고, 나쁜 프롬프트는 그중 아무것도 담지 않은 한 덩어리였다. 프롬프트가 구체적일수록 돌아오는 코드가 작아지고, 코드가 작을수록 읽고 대조하기 쉬워진다. 그것이 이 수업의 역할 분담(코드는 AI, 검증은 사람)이 프롬프트 층위에서 작동하는 방식이다.
부록 E: 평가·루브릭 전문
이 부록은 이 수업의 평가 규정 전체를 한자리에 모은 참조 문서다. 여기 담긴 항목은 강의계획서에 명문화되며, 1주차에 그 전문이 배포된다. 채점 기준을 미리 공개하는 것이 이 수업의 설계 원칙이다. 매주 실습이 최종 시험의 축소판(mini practical)으로 설계되어 있으므로, 루브릭은 첫 주부터 학습 목표 역할을 한다. 총점은 100점이고, 세 번의 시험이 80점, 과제와 출석이 각 10점이다.
E.1 평가 한눈에 보기
| 평가 | 배점 | 시기 | AI | 형식 |
|---|---|---|---|---|
| 시험1 | 20 | 7주 | 금지 | 오프라인 지필 |
| 시험2 | 20 | 12주 | 필수 | 90분 현장 실기 |
| 시험3(최종 시험) | 40 | 15주 + 시험주간 | 필수 | 2시간 바이브코딩 + GitHub + 구술 방어 |
| 과제 | 10 | 상시 | 허용 | LLM 위키 6점 + 서브프로젝트 4점 |
| 출석 | 10 | 상시 | — | 실습 대면 5 + 이론 LMS 5 |
평가마다 AI를 쓸 수 있는지가 다르게 정해져 있고, 이 구분을 AI 활용 규칙이라 한다. 시험2와 최종 시험은 AI 사용이 필수이고, 과제는 허용이며, 시험1만 AI를 전면 금지한다. 시험1이 반대 방향에 놓인 데에는 이유가 있다. 최종 시험이 전부 AI를 쓰는 시험이므로, AI 없이 남는 개인의 능력(코드를 읽고 검증하고 의사코드를 설계하는 힘)을 따로 한 번 확인하는 장치가 필요하다. 시험1이 그 자리를 맡는다.
모든 평가물에는 AI 사용내역 선언문(AI use declaration) 1쪽을 첨부한다. 양식은 E.7에 있다. 첨부하지 않은 제출물은 부정행위로 처리되므로, 선언문은 채점 이전에 통과해야 하는 관문이다.
E.2 시험1: 20점 (7주, AI 금지 지필)
시험1은 1~6주 내용을 누적 범위로 하는 오프라인 지필 시험이다. 컴퓨터도 AI도 쓰지 않고 종이 위에서 푼다. 문항 유형은 네 가지이며, 전부 그때까지의 스스로 점검·연습문제 Q군에서 매주 연습한 형식이다.
- 생성 코드의 출력을 예측하라. (AI가 낸 코드를 손으로 따라 실행해 결과를 적는다)
- 제시된 AI 생성 코드의 목적과 동작을 한국어로 설명하라. (코드 읽기, EiPE 유형)
- AI가 낸 오답을 채점·교정하라. (단위 오류·날조한 물성값이 심어져 있다)
- 주어진 화공 계산문제를 풀 의사코드를 설계하라. (코드가 아니라 절차를 쓴다)
AI가 코드를 대신 써 주는 수업에서 지필 시험이 왜 있는지 의아할 수 있다. 돌아가는 코드를 만드는 것과 그 코드를 이해하는 것은 다른 능력이고, 시험1은 뒤의 것만 따로 잰다. "돌아가는데 설명은 못 하는" 상태를 이 수업이 가장 경계하기 때문이다.
E.3 시험2: 20점 (12주, AI 필수 실기 90분)
시험2는 90분 동안 현장에서 치르는 실기 시험이다. 당일 주제가 공개되고 학번별 파라미터가 주어진다. 시작과 동시에 빈 저장소에 첫 커밋을 남기고, 90분 안에 커밋을 4회 이상 쌓아야 한다. 과제는 소형 도구 제작과 고장 난 코드의 디버깅이다.
채점은 두 갈래로 나뉜다. 자동 스모크 테스트가 12점, 과정 점수가 8점이다. 과정 점수는 커밋 타임라인과 세션 로그의 정합성을 본다. 시험2의 형식·인프라·규정은 최종 시험과 동일하게 맞춰져 있다. 시험2 자체가 최종 시험의 4분의 3 스케일 리허설이라는 뜻이며, 여기서 겪는 장애와 실수가 3주 뒤 40점짜리 시험을 대비하는 경험이 된다.
E.4 시험3: 40점 (15주 + 시험주간)
최종 시험은 이 수업의 마지막 평가다. 당일 주제를 받아 2시간 동안 바이브코딩으로 도구를 완성하고, GitHub에 업로드한 뒤, 시험주간에 구술 방어를 치른다.
E.4.1 주제 규정
주제는 시험 당일 공개된다. 전원이 같은 구조의 문제를 받되 학번별로 파라미터(물질계·조업조건·데이터 시드)가 달라진다. 구조가 같으므로 주제 간 난이도는 등가이고, 파라미터가 다르므로 남의 답을 그대로 복제하는 경로는 사실상 막힌다. 본인 위키를 활용하는 자기참조형 주제는 형평성 문제로 시험 풀에서 빠지고, 대신 서브프로젝트 풀로 간다. 주제는 전부 8~13주의 도구 사다리 범위 안에서 출제된다. 다루지 않은 것은 평가하지 않는다.
출제 예시는 다음과 같다. 학번별 이성분계의 기포점·이슬점 계산기와 T-xy 작도(NIST 대조 검증 포함), 학번별 recycle 공정의 물질수지 정합성 검사와 흐름 시각화, 학번별 실험 데이터의 회귀와 이상치 자동 탐지 리포트 생성기다. 세 예시 모두 학기 중 만든 도구의 조합이거나 변형이다.
E.4.2 커밋 규정
최종 시험의 커밋 규정은 네 줄이며, 각 줄이 채점·부정행위 판별·장애 대비의 근거가 된다. Git 명령의 사용법은 부록 D에 있다.
- 시작 시 빈 저장소에 첫 커밋을 남긴다. (시작 시각의 증거이며, 첫 커밋이 완성본이면 외부 반입으로 자동 플래그된다)
- 30분마다 스냅샷 커밋을 남긴다. (장애 시 부분 채점의 근거이며, 미완성 커밋도 감점 요소가 아니다)
- 2시간 내 커밋 5회 이상을 채운다. (
git log --oneline으로 센다) - 종료 즉시 업로드하면 결과물이 동결된다. (종료 이후의 수정 커밋은 채점에서 제외된다)
E.4.3 루브릭
최종 시험의 40점은 네 차원으로 나뉜다. 이 표는 1주차에 사전 배포되므로, 학기 내내 매주 실습이 이 네 차원을 조금씩 훈련하는 셈이다.
| 차원 | 배점 | 세부 배점과 기준 |
|---|---|---|
| 작동성 | 14 | README대로 실행 5 / 핵심 기능 6 / 예외 처리 3. 검증 증빙(단위·대조값·극한값의 README 기록)은 필수 항목이다. |
| 코드 이해 | 10 | 무작위 지목 설명 4 / "이 줄을 바꾸면?" 예측 3 / 한계·개선점 3. |
| 프롬프트 전략 | 8 | 문제 분해·반복 개선 4 / 디버깅 프롬프트 2 / 검증 습관 2. 판정 기준은 게이트웨이 서버측 로그이며, 학생이 제출한 로그는 대조용이다. |
| 발표(구술 방어) | 8 | 문제정의 → 데모 → 화공 해석 3분 구성 5 / Q&A 3. |
코드 이해 차원에는 사전 공지된 조작적 정의 하나가 걸려 있다. 구술 방어에서 채점자가 코드의 특정 부분을 무작위로 지목해 설명을 요구하는데, 지목 질문 3개 중 2개 이상을 설명하지 못하면 이 차원은 0점이 된다. 부분 점수가 아니라 0점이다. "설명 못 하면 이해 점수 0점"이라는 1주차 공지가 바로 이 규정이며, 작동하는 결과물이 있어도 그것을 자기 말로 설명하지 못하면 이해 점수는 남지 않는다.
구술 방어는 시험주간에 분리해 치른다. 1인당 3분 데모와 2분 지목 구술로 진행되며, 60명이면 두세 세션으로 나눠 소화한다. 결과물 채점(스모크 테스트와 README 검증)과 구술 방어를 다른 날로 떼어 놓았기 때문에, 채점자는 한 사람당 결과물에 약 15분, 구술에 5분을 온전히 쓸 수 있다.
E.5 과제: 10점 (위키 6 + 서브프로젝트 4)
과제 10점은 LLM 위키 6점과 서브프로젝트 4점으로 나뉜다. 배점이 없는 필수 활동은 부실해지기 쉬우므로, 두 축 모두에 점수를 건다.
E.5.1 LLM 위키: 6점
위키 점수는 5주·10주·15주의 마일스톤 3회에 각 2점씩 매겨진다. 각 마일스톤은 세 가지 축으로 본다.
| 축 | 보는 것 |
|---|---|
| 누적성 | 주당 의미 있는 커밋 1회 이상. 몰아치기는 자동 감점된다. 커밋 이력이 곧 증거다. |
| 개인화 깊이 | 본인의 에러 스크린샷, "내가 틀렸던 것" 섹션, 타 과목과의 연결이 필수다. |
| 활용도 | 시험 저장소에 본인 위키 인용 링크를 남긴다. 활용의 증거 형식이 명문화되어 있다. |
채점은 전원에 대해 자동 지표를 1차로 적용한다. 커밋 리듬과 구조 요건을 스크립트가 집계하며, 구두 스팟체크는 이상 신호가 잡힌 경우의 확인용 2차로만 쓴다. 표본을 불평등하게 뽑지 않기 위한 설계다. 성장단계 태그(#seed → #growing → #evergreen)의 evergreen 승격 수와 링크 밀도도 마일스톤마다 자동 집계된다.
여기서 몰아치기가 통하지 않는 이유는 규정보다 구조에 있다. 2~4주에 만든 정리기와 플래시카드 생성기가 위키를 직접 읽고 쓰므로, 도구를 쓰면 위키가 자란다. 마지막 주에 복사·붙여넣기로 채운 위키는 이 커밋 리듬을 만들지 못한다.
E.5.2 서브프로젝트: 4점
서브프로젝트는 9주에 선언하고 13주에 스프린트를 거쳐 14주 발표회에서 마무리한다. 배점은 결과물 2점과 발표 2점이다. 결과물 점수는 작동 여부와 검증 증빙을 보고, 발표 점수는 3분 데모영상과 라이브 Q&A를 본다. 공통 요건으로 본인의 사다리 산출물을 2개 이상 재사용해야 한다. 도구를 1회성 과제가 아니라 복리로 쌓이는 자산으로 다루게 하려는 요건이다.
E.6 출석: 10점
출석은 실습 5점과 이론 5점으로 나뉜다. 실습 5점은 현장 대면으로 확인한다. 이론 5점은 LMS 시청 로그로 산정하며, 차시별 러닝타임 대비 시청 비율은 교무처 확인값을 적용한다. 차시마다 붙는 마이크로 퀴즈는 성적과 무관한 시청 증빙용임이 명시된다. 퀴즈 오답이 출석 감점으로 이어지면 규정 근거를 별도로 요구받기 때문이다.
E.7 AI 사용내역 선언문 양식
모든 평가물에 이 선언문 1쪽을 첨부한다. AI를 쓰지 않은 평가(시험1)에도 "사용 안 함"으로 첨부한다. 미첨부는 부정행위로 처리된다. 양식은 아래와 같으며, 오른쪽 칸을 채워 제출한다.
| 항목 | 기재란 |
|---|---|
| 평가명 · 제출물 (시험1·2·3 / 과제 / 서브프로젝트 중) | |
| 학번 · 이름 | |
| 이 평가의 AI 활용 규칙 (금지 / 필수 / 허용 중 해당) | |
| 사용한 AI 도구 (없으면 "없음") | |
| AI에게 시킨 부분 (어떤 작업을, 대략 어떤 프롬프트로) | |
| 내가 직접 한 부분 (문제 분해 · 코드 읽기 · 검증) | |
| 검증 3종 수행 내역 (단위 체크 · 대조값 · 극한값) | |
| 위 내용이 사실임을 확인함 (서명 · 날짜) |
선언문은 감시 장치가 아니라 이 수업의 역할 분담을 문서로 옮긴 것이다. AI가 코드를 쓰고 학생이 분해·검증·설명을 책임진다는 분담이 선언문의 두 칸, "AI에게 시킨 부분"과 "내가 직접 한 부분"으로 나타난다. 두 칸을 정직하게 채울 수 있다면 그 자체가 코드 이해 점수와 프롬프트 전략 점수의 예습이다.
E.8 실기시험 비상 프로토콜
시험2와 최종 시험은 AI 게이트웨이와 네트워크에 의존하므로, 당일 장애를 전제로 방어선이 짜여 있다. 이 프로토콜도 강의계획서에 명문화된다.
- 예비 API 제공자 1개를 게이트웨이에 상시 등록해 둔다. (한 제공자가 죽어도 전환된다)
- 시험은 유선랜 전산실습실에서 치른다. (무선 혼잡을 배제한다)
- 예비 노트북 2~3대를 대기시킨다. (기기 장애 학생을 즉시 구제한다)
- 30분 스냅샷 커밋이 부분 채점의 근거가 된다. (E.4.2 규정과 같은 커밋이 보험이 된다)
- 전면 장애 시 업로드를 24시간 연장하고, 발표(구술 방어)만 당일 진행한다.
이 다섯 줄은 시험 당일 무언가 고장 나더라도 그 손해가 학생에게 돌아가지 않게 하려는 설계다. 학생이 할 일은 규정대로 30분마다 커밋을 남기는 것 하나이며, 나머지는 프로토콜이 받아 준다. 그래서 시험장에서 장애가 나면 당황해 자리를 뜨지 말고, 손을 들어 감독에게 알리는 것이 규정에 맞는 첫 행동이다.
참고문헌·출처
본 교재의 집필에는 아래 오픈 라이선스 공개 자료를 구조·사실 근거 코퍼스로 참조하였다. 각 항목의 라이선스는 수집 시점(2026-07-16, KST)에 원 사이트·저장소에서 직접 확인한 것이다.
프로그래밍 교재 코퍼스
- Downey, Allen B. Think Python: How to Think Like a Computer Scientist, 2nd ed. Green Tea Press. greenteapress.com/wp/think-python-2e · CC BY-NC 3.0 Unported
- Severance, Charles R. Python for Everybody: Exploring Data in Python 3. py4e.com · CC BY 4.0(사이트) / CC BY-NC-SA 3.0(도서 본문)
- Sweigart, Al. Automate the Boring Stuff with Python, 3rd ed. automatetheboringstuff.com · CC BY-NC-SA 3.0
- DeNero, John. Composing Programs. composingprograms.com · CC BY-NC-SA 3.0
화학공학 교재 코퍼스
- Introduction to Chemical Engineering Processes. Wikibooks. en.wikibooks.org · CC BY-SA 3.0
- Engineering LibreTexts 교재군(eng.libretexts.org): Foundations of Chemical and Biological Engineering I; Distillation Science; Phase Relations in Reservoir Engineering; Fluid Mechanics (Bar-Meir); Slurry Transport (Miedema) · 페이지별 CC BY 4.0 · CC BY-NC-SA 4.0 · GNU FDL 1.3 (각 페이지 명기 기준)
- NIST Chemistry WebBook, NIST Standard Reference Database 69. webbook.nist.gov · 미국 연방정부 표준참조데이터(공공 접근); 물성 대조값의 1차 출처
계산공학·ML 코퍼스
- Kitchin, John R. pycse: Python Computations in Science and Engineering. github.com/jkitchin/pycse · 저장소 GPL-2.0 / 북 챕터 GNU FDL 1.3
- Ulissi, Zachary W. CMU 06-325: Machine Learning for Chemical Engineers (Fall 2022 강의자료). github.com/ulissigroup/F22-06-325 · CC BY 4.0
- Kitchin, John R. CMU 06-642: Data Science and Machine Learning in Chemical Engineering (Spring 2026 공개 강의자료). github.com/jkitchin/s26-06642 · 명시 라이선스 없음(저자 공개 게시); 구조 참조에 한정 사용
- Fangohr, Hans. Introduction to Python for Computational Science and Engineering. github.com/fangohr · CC BY-NC 4.0
판권 고지
본 교재는 위 오픈 라이선스 자료를 구조·사실 근거로 참조하여 신규 집필하였음. 원문 문장의 번역 전재가 아니라 장치 설계(챕터 구조·연습문제 형식·검증 루틴)와 사실 관계(물성값·식·라이브러리 동작)의 근거로 사용하였음.
NC/SA 조항 소스 포함: 참조 코퍼스에 CC BY-NC(비영리)·SA(동일조건변경허락) 조항이 붙은 자료가 포함되어 있으므로, 본 교재의 상업적 재배포 전 라이선스 검토가 필요함. 수업 내 비영리 배포는 해당 조항과 충돌하지 않음.
『화학공학을 위한 AI: 바이브코딩으로 배우는 화공 도구 제작』 v3.0 · 선세호 · 영남대학교 화학공학부 · 2026-09-02