CH01시뮬레이터 시작하기
HPLC가 무엇을 하는 장비인지 큰 그림을 잡고, 시뮬레이터를 실행해 3D 화면 · 제어판 · Python Playground의 구성을 익힙니다.
학습 목표#
- 이동상 · 정지상 · 분배라는 말로 HPLC가 물질을 분리하는 원리를 설명할 수 있습니다.
- 역상 C18 컬럼에서 극성이 낮은 물질이 더 늦게 나오는 이유를 말할 수 있습니다.
- 시료가 지나가는 길(용매병 → 탈기 → 펌프 → 오토샘플러 → 컬럼 → 검출기 → 폐액)을 순서대로 적을 수 있습니다.
- 크로마토그램에서 머무름 시간(tR) · 피크 면적 · 피크 높이 · 베이스라인을 찾아 읽을 수 있습니다.
- 시뮬레이터를 실행하고 3D 장비 뷰 · 상태 패널 · 크로마토그램 · 이벤트 로그 · Python Playground가 어디에 있는지 찾을 수 있습니다.
이론#
1교시 — HPLC는 무엇을 하는 장비인가#
고성능 액체 크로마토그래피(High-Performance Liquid Chromatography, HPLC) 는 섞여 있는 여러 물질을 분리하고, 각 물질이 얼마나 들어 있는지 재는 장비입니다. 커피 속 카페인, 약 속 유효 성분, 물 속 오염 물질 등을 분석할 때 씁니다.
원리는 세 단어로 정리됩니다.
| 용어 | 뜻 | HPLC에서의 실체 |
|---|---|---|
| 이동상(mobile phase) | 흐르면서 시료를 실어 나르는 액체 | 물 + 아세토니트릴(또는 메탄올) 혼합 용매 |
| 정지상(stationary phase) | 움직이지 않고 시료를 붙잡는 물질 | 컬럼 안에 채운 작은 실리카 입자 표면의 C18 사슬 |
| 분배(partition) | 물질이 이동상과 정지상 사이를 오가며 나뉘는 것 | 정지상 쪽에 오래 머무는 물질일수록 늦게 나옴 |
물질마다 정지상을 "좋아하는 정도"가 다릅니다. 정지상에 오래 머무는 물질은 컬럼을 늦게 빠져나오고, 이동상에 주로 머무는 물질은 빨리 빠져나옵니다. 이렇게 나오는 시간의 차이로 물질이 분리됩니다. 이 정도를 숫자로 나타낸 것이 머무름 계수(retention factor, k) 입니다.
tR = t0 × (1 + k)— t0 는 정지상에 전혀 붙지 않는 물질이 컬럼을 빠져나오는 시간(불감 시간, dead time)입니다.- k = 0 이면 t0 에 바로 나오고, k 가 클수록 늦게 나옵니다.
복도(컬럼)를 지나가는 학생들(물질)을 떠올려 보세요. 복도 벽에 게시판(정지상)이 붙어 있습니다. 게시판에 관심이 많은 학생은 자주 멈춰 서서 늦게 도착하고, 관심이 없는 학생은 곧장 지나갑니다. 사람들의 흐름(이동상)은 모두에게 같습니다.
역상(Reversed-Phase) C18 컬럼#
가장 많이 쓰는 방식은 역상 크로마토그래피입니다. "역상"이라는 이름은 옛날 방식(극성 정지상 + 비극성 이동상)과 극성이 반대라서 붙었습니다.
- 정지상: 실리카 입자 표면에 탄소 18개짜리 사슬(옥타데실, C18)을 붙인 것 → 비극성(기름 같은 성질)
- 이동상: 물 + 유기 용매 → 극성
비슷한 것끼리 잘 어울리므로(like dissolves like), 극성이 낮은(기름에 가까운) 물질은 C18 사슬에 오래 붙어 있어 늦게 나오고, 극성이 높은(물에 잘 녹는) 물질은 이동상과 함께 빨리 나옵니다. 이동상에 유기 용매 비율을 높이면 이동상 자체가 덜 극성이 되어 모든 물질이 빨리 나옵니다 (이동상 조성을 바꾸는 방법은 ch03에서 다룹니다).
# 머무름 계수 k 와 머무름 시간: tR = t0 * (1 + k)
t0 = 1.0 # 머무르지 않는 물질이 컬럼을 빠져나오는 시간 (min, 가정값)
# 가상의 물질 3개. k 가 클수록 정지상(C18)에 오래 붙어 있습니다.
compounds = {
"A (극성 큼)": 0.8,
"B (중간)": 2.5,
"C (극성 작음)": 6.0,
}
for name, k in compounds.items():
tR = t0 * (1 + k)
in_stationary = k / (1 + k) * 100 # 정지상에 머문 시간 비율 (%)
print(f"{name:<12} k = {k:4.1f} tR = {tR:4.1f} min 정지상 체류 {in_stationary:4.1f} %")실행하면 극성이 작은 C가 7.0 min으로 가장 늦게 나오고, 시간의 약 86 %를 정지상에 붙어 보낸다는 것을 확인할 수 있습니다.
시료가 지나가는 길#
| 순서 | 장치 | 하는 일 |
|---|---|---|
| ① | 용매병 | 이동상(예: A = 물, B = 아세토니트릴)을 담아 둡니다. |
| ② | 탈기(degasser) | 용매에 녹은 공기를 빼서 펌프와 검출기에 기포가 생기지 않게 합니다. |
| ③ | 펌프 | 용매를 정해진 비율로 섞고, 정해진 유속(mL/min)으로 밀어냅니다. 컬럼 저항 때문에 압력이 생깁니다. |
| ④ | 오토샘플러 | 바이알에서 시료를 정해진 부피(µL)만큼 뽑아 흐르는 이동상에 넣습니다(주입, injection). |
| ⑤ | 컬럼 | 시료 속 물질이 분배되며 서로 다른 시간에 빠져나옵니다. 온도조절기 안에 넣어 온도를 일정하게 둡니다. |
| ⑥ | 검출기 | 빠져나온 물질이 빛(UV)을 얼마나 흡수하는지 재서 전기 신호로 바꿉니다. |
| — | 폐액 | 검출기를 지난 액체는 폐액 통으로 갑니다. 시료는 다시 쓰지 않습니다. |
크로마토그램 읽기#
검출기 신호를 시간에 따라 그린 그래프가 크로마토그램(chromatogram) 입니다.
| 요소 | 읽는 법 | 알려 주는 것 |
|---|---|---|
| 베이스라인(baseline) | 물질이 없을 때의 평평한 바닥선 | 장비가 안정했는지 (흔들리면 기포 · 온도 변화 의심) |
| 머무름 시간(tR) | 주입부터 피크 꼭대기까지의 시간 | 무슨 물질인지 (같은 조건이면 같은 물질은 같은 tR) |
| 피크 높이(height) | 베이스라인에서 꼭대기까지 | 양에 비례 (보조 지표) |
| 피크 면적(area) | 피크와 베이스라인 사이 넓이 | 얼마나 들어 있는지 (정량의 기본 값) |
정성(무엇인가)은 tR로, 정량(얼마나인가)은 주로 면적으로 판단합니다. 면적으로 농도를 구하는 검량선은 ch07에서 다룹니다.
import numpy as np
# 가상의 크로마토그램: 가우스 모양 피크 2개 + 약간의 잡음
dt = 0.01 # 0.01 min 간격으로 기록
t = np.arange(0, 6, dt) # 0 ~ 6 min
rng = np.random.default_rng(0)
def peak(t, tR, height, sigma):
return height * np.exp(-0.5 * ((t - tR) / sigma) ** 2)
baseline = 0.5 # 베이스라인 (mAU)
signal = (baseline
+ peak(t, 1.8, 40.0, 0.05) # 극성이 큰 물질: 먼저 나옴
+ peak(t, 3.6, 25.0, 0.08) # 극성이 작은 물질: 늦게 나옴
+ rng.normal(0, 0.2, t.size))
def measure(t0, t1):
m = (t >= t0) & (t < t1)
y = signal[m] - baseline # 베이스라인을 빼고 잽니다
i = np.argmax(y)
return t[m][i], y[i], np.sum(y) * dt # tR, 높이, 면적(mAU*min)
for name, (t0, t1) in {"피크 1": (1.4, 2.2), "피크 2": (3.1, 4.1)}.items():
tR, h, a = measure(t0, t1)
print(f"{name}: tR = {tR:.2f} min, 높이 = {h:.1f} mAU, 면적 = {a:.2f} mAU*min")
# 글자로 그린 크로마토그램 (1.0 ~ 4.4 min, 0.1 min 마다 한 줄)
print("\n t(min) 신호")
for k in range(100, 440, 10):
bar = "#" * int(max(signal[k], 0))
print(f"{t[k]:5.1f} {bar}")두 피크의 높이는 40과 25로 다르지만 면적은 둘 다 약 5.0 입니다. 늦게 나온 피크는 넓게 퍼지기 때문입니다. "높이가 크면 양이 많다"고 단정하면 안 되는 이유가 여기에 있습니다.
애질런트 1260 Infinity II 모듈 스택#
애질런트(Agilent) 1260 Infinity II 는 기능별 모듈을 위아래로 쌓아(stack) 하나의 시스템으로 씁니다. 이 시뮬레이터는 아래 구성을 기본으로 모사합니다. 실제 실험실 장비는 모듈 종류(예: DAD 검출기, 다른 펌프)가 다를 수 있으니 장비 앞판의 라벨로 확인합니다.
| 모듈 | 모델 번호 | 역할 | 유로에서의 위치 |
|---|---|---|---|
| 4원 펌프 (Quaternary Pump) | G7111B | 용매 최대 4종을 섞어 송액, 탈기 장치 내장 | ② ③ |
| 바이알 샘플러 (Vialsampler) | G7129A | 바이알 시료를 자동 주입 | ④ |
| 다중 컬럼 온도조절기 (Multicolumn Thermostat, MCT) | G7116A | 컬럼 온도 유지 | ⑤ |
| 가변파장 검출기 (Variable Wavelength Detector, VWD) | G7114A | 한 파장(또는 몇 개)의 UV 흡광도 측정, 중수소 램프 사용 | ⑥ |
| 용매 캐비닛 (Solvent Cabinet) | — | 용매병 · 폐액 받침 | ① |
최대 압력 · 유속 범위 같은 세부 사양은 모델과 옵션에 따라 다르므로 이 차시에서는 외우지 않습니다. 필요할 때 정보 카드와 아래 참고자료의 공식 사용자 설명서를 확인합니다 (펌프 사양은 ch03, 샘플러 · 컬럼 · 검출기 사양은 ch04).
시뮬레이터로 익히는 것, 실물에서만 익히는 것#
| 시뮬레이터로 충분히 익힐 수 있는 것 | 실물에서만 익힐 수 있는 것 |
|---|---|
| 모듈 이름 · 위치 · 유로 순서 | 피팅을 손으로 조일 때의 감각(너무 세게 · 너무 약하게) |
| 압력 · 유속 · 온도 · 램프 상태 읽기 | 누설(leak) — 피팅 주변의 작은 물방울, 마른 염 결정 |
| 메서드 · 시퀀스 입력 순서 | 용매 냄새 · 환기, 장갑 · 보안경 착용 습관 |
| 크로마토그램 해석, 계산 | 펌프 소음 · 진동의 변화로 이상 알아차리기 |
| 잘못된 조작의 결과를 안전하게 미리 보기 | 실제 폐액 처리, 용매병 교체 |
이 강좌는 실물 장비를 쓰기 전에 순서와 개념을 몸에 익히는 것이 목적입니다. 실물 HPLC를 처음 사용할 때는 반드시 실험실 담당자의 교육을 받고, 실험실 안전 수칙(보안경 · 장갑 · 용매 취급)을 따르세요.
2교시 — 시뮬레이터 실행과 화면 구성#
이 단원에서 할 일#
강좌 사이트를 열고, 시뮬레이터 화면의 네 영역과 Python Playground의 위치 · 조작법을 익힙니다.
사이트 접속 또는 로컬 실행#
배포된 강좌 사이트 주소가 있으면 브라우저로 바로 접속합니다. 내 PC에서 실행하려면 다음 순서를 따릅니다.
- Python 3 가 설치되어 있는지 확인합니다. 명령 프롬프트(<kbd>Win</kbd>+<kbd>R</kbd> →
cmd→ <kbd>Enter</kbd>)에서python --version을 입력합니다. - 강좌 저장소 폴더(맨 위에
build.py가 있는 폴더)로 이동합니다. - 아래 명령을 실행합니다. Windows에서는 저장소에
start.bat이 있으면 더블클릭해도 같습니다. 미리보기: http://localhost:8000 (Ctrl+C 로 종료)라는 줄이 나오면 브라우저 주소창에http://localhost:8000을 입력합니다.- 다 쓴 뒤에는 명령 창에서 <kbd>Ctrl</kbd>+<kbd>C</kbd> 를 눌러 서버를 끕니다.
cd 강좌저장소폴더
python build.py --serve미리보기: http://localhost:8000 (Ctrl+C 로 종료)3D 장비 모델과 시뮬레이터 화면은 강좌 진행에 맞춰 추가됩니다. 사이드바나 차시 머리에 3D 뷰어 · 시뮬레이터 버튼이 보이지 않으면 아직 준비 중인 것입니다. 그동안은 이 차시의 Playground 실습(numpy 계산)으로 같은 내용을 익히고, 모델이 준비되면 아래 따라하기를 다시 해 봅니다.
화면 구성#
| 영역 | 이름 | 보는 것 |
|---|---|---|
| 위 | 상단 도구 막대 | 유로 애니메이션 재생, 뷰 초기화, 앞판 열기/닫기, Playground 열기 |
| A | 3D 장비 뷰 | 모듈 스택과 배관. 돌려 보고 모듈을 클릭합니다. |
| B | 모듈 상태 패널 | 압력(bar) · 유속(mL/min) · 컬럼 온도(°C) · 램프 상태 · 모듈 상태(Ready/Run/Error) |
| C | 실시간 크로마토그램 | 분석 중 검출기 신호. 위 1교시의 그래프와 같은 모양입니다. |
| D | 이벤트 로그 | 펌프 켜짐, 램프 켜짐, 주입, 오류 같은 사건이 시간순으로 쌓입니다. |
압력이 갑자기 0이 되면 B 패널에서 먼저 알아차리고, 왜 그런지는 D 이벤트 로그에서 찾습니다. 실물 장비의 데이터 시스템도 같은 구조(상태 표시 + 로그북)를 가지므로 지금부터 습관을 들여 두면 좋습니다.
3D 뷰 조작#
- 3D 장비 뷰(A) 안에 마우스 포인터를 올립니다.
- 왼쪽 버튼을 누른 채 끌면 장비 둘레로 시점이 회전합니다. 좌우로 끌어 측면 · 후면을 봅니다.
- 오른쪽 버튼을 누른 채 끌면 화면이 평행 이동합니다. 배관이 화면 밖으로 나갔을 때 씁니다.
- 마우스 휠을 굴려 확대/축소합니다. 피팅 연결부를 가까이 볼 때 씁니다.
- 시점이 엉켰으면 상단 도구 막대의 뷰 초기화 버튼을 눌러 정면 시점으로 돌아옵니다.
- 모듈을 클릭하면 정보 카드(모델 번호 · 역할 · 주요 사양)가 열립니다. 카드의 닫기(×)로 닫습니다.
- 모듈을 선택한 상태에서 앞판 열기/닫기를 누르면 앞 덮개가 열려 안쪽 부품(예: 펌프 헤드, 샘플러 니들, 컬럼)을 볼 수 있습니다.
Python Playground#
Playground는 브라우저 안에서 Python(Pyodide)을 실행하는 편집기입니다. 이 강좌의 계산 실습과, 시뮬레이터를 코드로 조작하는 실습에 씁니다.
- 사이드바 또는 차시 머리의 Python 버튼을 누르거나, 코드 블록의 Playground에서 열기 버튼을 누릅니다.
- 처음 열 때 Python 실행 환경을 내려받느라 몇 초 ~ 수십 초 걸립니다. ▶ 실행 버튼이 눌리는 상태가 될 때까지 기다립니다.
- 편집기에 코드를 쓰고 ▶ 실행(<kbd>Ctrl</kbd>+<kbd>Enter</kbd>)을 누릅니다. 결과는 출력 창에 나옵니다.
- 오래 도는 코드는 ■ 정지 버튼으로 멈춥니다. 시간 제한(초) 이 지나도 자동으로 멈춥니다.
- 위쪽 타깃 목록에서
Simulator를 고르고 오른쪽 상태 표시를 확인합니다. 시뮬레이터가 준비되지 않았으면 "찾을 수 없음"이 표시되며, 이 차시의 numpy 실습은 타깃과 관계없이 실행됩니다.
| 동작 | 방법 |
|---|---|
| 코드 실행 | ▶ 실행 또는 <kbd>Ctrl</kbd>+<kbd>Enter</kbd> |
| 실행 멈춤 | ■ 정지 |
| 편집기에서 빠져나오기 | <kbd>Esc</kbd> |
| 3D 회전 / 이동 / 확대 | 왼쪽 드래그 / 오른쪽 드래그 / 휠 |
| 브라우저 강력 새로고침(캐시 무시) | <kbd>Ctrl</kbd>+<kbd>F5</kbd> (또는 <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>R</kbd>) |
| 로컬 서버 끄기 | 명령 창에서 <kbd>Ctrl</kbd>+<kbd>C</kbd> |
브라우저 권장 사양#
- Chrome 또는 Edge 최신 버전을 권장합니다. 3D 화면은 WebGL 을 사용합니다.
- 노트북은 전원을 연결하고, 3D 화면이 끊기면 다른 탭을 닫습니다.
- 학교 · 회사 PC에서 하드웨어 가속이 꺼져 있으면 3D 화면이 비어 보일 수 있습니다(아래 오류 1번).
lessons/ch01.html같은 파일을 탐색기에서 더블클릭해file://주소로 엽니다 → 3D 모델 · Playground가 동작하지 않습니다. 반드시http://localhost:8000으로 엽니다.- 서버를 켠 명령 창을 닫아 버립니다 → 페이지가 "연결할 수 없음"이 됩니다. 실습이 끝날 때까지 창을 열어 둡니다.
- 3D 뷰에서 휠을 너무 많이 굴려 장비가 사라졌다고 생각합니다 → 뷰 초기화를 누릅니다.
실습 (시뮬레이터)#
- 시뮬레이터 상단 도구 막대에서 ▶ 유로 애니메이션을 누릅니다. (시뮬레이터가 아직 없으면 위 그림 "HPLC 유로"를 보고 합니다.)
- 이동상이 빛나는 점으로 흐르는 것을 보며, 지나가는 장치를 6단계로 받아 적습니다.
- 아래 코드의
my_notes에 적은 순서를 넣고 실행해 맞았는지 확인합니다.
# 시료가 지나가는 길: 6단계 순서 확인하기
answer = ["용매병", "탈기", "펌프", "오토샘플러", "컬럼", "검출기"]
# TODO: 유로 애니메이션을 보며 받아 적은 순서로 아래 리스트를 고치세요
my_notes = ["용매병", "펌프", "탈기", "컬럼", "오토샘플러", "검출기"]
for i, (a, b) in enumerate(zip(answer, my_notes), start=1):
mark = "OK" if a == b else "XX"
print(f"{i}단계 [{mark}] 정답={a:<8} 내 기록={b}")
score = sum(a == b for a, b in zip(answer, my_notes))
print(f"\n맞힌 단계: {score}/6")탈기는 펌프 앞(용매에서 공기를 먼저 빼야 펌프에 기포가 들어가지 않음), 주입은 컬럼 앞(시료가 컬럼을 지나야 분리됨)입니다.
answer = ["용매병", "탈기", "펌프", "오토샘플러", "컬럼", "검출기"]
my_notes = ["용매병", "탈기", "펌프", "오토샘플러", "컬럼", "검출기"]
score = sum(a == b for a, b in zip(answer, my_notes))
print(f"맞힌 단계: {score}/6")
print(" -> ".join(my_notes + ["폐액"]))아래 코드는 피크 3개짜리 가상 크로마토그램을 만듭니다. TODO 를 채워 각 피크의 tR · 높이 · 면적을 출력하고, 면적이 가장 큰 피크를 고르세요.
import numpy as np
dt = 0.01
t = np.arange(0, 8, dt)
baseline = 1.0
def peak(t, tR, height, sigma):
return height * np.exp(-0.5 * ((t - tR) / sigma) ** 2)
signal = (baseline + peak(t, 1.5, 60, 0.04)
+ peak(t, 3.2, 30, 0.10)
+ peak(t, 5.8, 20, 0.20))
windows = {"피크 1": (1.0, 2.0), "피크 2": (2.6, 3.8), "피크 3": (4.8, 6.8)}
for name, (t0, t1) in windows.items():
m = (t >= t0) & (t < t1)
y = signal[m] - baseline
# TODO: tR(꼭대기 시간), height(꼭대기 높이), area(면적 = 합 x dt)를 구하세요
tR = 0
height = 0
area = 0
print(f"{name}: tR={tR:.2f} min 높이={height:.1f} 면적={area:.2f}")import numpy as np
dt = 0.01
t = np.arange(0, 8, dt)
baseline = 1.0
def peak(t, tR, height, sigma):
return height * np.exp(-0.5 * ((t - tR) / sigma) ** 2)
signal = (baseline + peak(t, 1.5, 60, 0.04)
+ peak(t, 3.2, 30, 0.10)
+ peak(t, 5.8, 20, 0.20))
windows = {"피크 1": (1.0, 2.0), "피크 2": (2.6, 3.8), "피크 3": (4.8, 6.8)}
result = {}
for name, (t0, t1) in windows.items():
m = (t >= t0) & (t < t1)
y = signal[m] - baseline
i = np.argmax(y)
tR, height, area = t[m][i], y[i], np.sum(y) * dt
result[name] = area
print(f"{name}: tR={tR:.2f} min 높이={height:.1f} 면적={area:.2f}")
print("면적이 가장 큰 피크:", max(result, key=result.get))가장 높은 피크 1(높이 60)이 아니라, 가장 낮지만 넓은 피크 3의 면적(약 10.0)이 가장 큽니다.
역상 C18 컬럼에서 같은 이동상 조건으로 분석할 때, 가장 늦게 나올 가능성이 큰 물질은?
- 물에 아주 잘 녹는 극성 물질
- 극성이 낮아 기름에 잘 녹는 물질
- 이동상과 똑같은 물질
- 정지상에 전혀 붙지 않는 물질
정답 보기
② — C18 정지상은 비극성이라 극성이 낮은 물질과 잘 어울립니다. 정지상에 오래 머물수록(k가 클수록) tR이 길어집니다. ④는 t0에 가장 먼저 나옵니다.크로마토그램에서 시료 속 물질이 얼마나 들어 있는지 판단할 때 기본으로 쓰는 값은?
- 머무름 시간(tR)
- 베이스라인의 높이
- 피크 면적
- 분석 총 시간
정답 보기
③ — 피크 면적은 검출기를 지나간 물질의 총량에 비례합니다. tR은 "무슨 물질인가"(정성)를 알려 주고, 베이스라인은 장비 안정성을 보여 줍니다. 실습 B처럼 높이만 보면 넓은 피크의 양을 과소평가합니다.- 3D 장비 뷰에서 뷰 초기화를 눌러 정면에서 시작합니다.
- 왼쪽 드래그로 약 90° 씩 돌려 오른쪽 측면 → 후면 → 왼쪽 측면 을 차례로 봅니다. 후면에서 모듈 사이를 잇는 배관과 전원 · 통신 케이블 위치를 관찰합니다.
- 정면으로 돌아와 모듈을 하나씩 클릭하고 정보 카드에서 모델 번호를 찾습니다.
- 아래 코드의 표를 완성해 실행합니다. (3D 모델이 준비되지 않았다면 위 "모듈 스택" 그림과 표에서 찾습니다.)
# 모듈 정보 카드에서 찾은 모델 번호를 표로 정리하기
modules = [
# (스택 위치, 모듈 이름, 모델 번호, 역할)
("1층(맨 아래)", "Quaternary Pump", "G7111B", "용매 혼합 + 송액, 탈기 내장"),
("2층", "Vialsampler", "TODO", "TODO"),
("3층", "Multicolumn Thermostat", "TODO", "TODO"),
("4층", "Variable Wavelength Det.", "TODO", "TODO"),
]
print(f"{'위치':<10}{'모듈':<28}{'모델':<8}역할")
print("-" * 70)
for pos, name, model, role in modules:
print(f"{pos:<10}{name:<28}{model:<8}{role}")
todo = sum(row.count("TODO") for row in modules)
print(f"\n남은 TODO: {todo}개")modules = [
("1층(맨 아래)", "Quaternary Pump", "G7111B", "용매 혼합 + 송액, 탈기 내장"),
("2층", "Vialsampler", "G7129A", "바이알 시료 자동 주입"),
("3층", "Multicolumn Thermostat", "G7116A", "컬럼 온도 유지"),
("4층", "Variable Wavelength Det.", "G7114A", "UV 흡광도 측정"),
]
print(f"{'위치':<10}{'모듈':<28}{'모델':<8}역할")
print("-" * 70)
for pos, name, model, role in modules:
print(f"{pos:<10}{name:<28}{model:<8}{role}")3D 뷰의 "회전"은 카메라를 장비 둘레로 돌리는 것입니다. 장비 중심을 원점, 정면 방향을 +z 로 두고, 카메라를 y축 둘레로 돌렸을 때 위치를 계산합니다. TODO 를 채워 정면 · 오른쪽 측면 · 후면 · 왼쪽 측면의 각도를 넣으세요.
import numpy as np
def rot_y(deg):
r = np.radians(deg)
return np.array([[ np.cos(r), 0, np.sin(r)],
[ 0, 1, 0 ],
[-np.sin(r), 0, np.cos(r)]])
camera = np.array([0.0, 0.3, 2.0]) # 장비 정면 2 m 앞, 0.3 m 위
# TODO: 각 면을 보는 회전 각도(deg)를 채우세요
views = {"정면": 0, "오른쪽 측면": None, "후면": None, "왼쪽 측면": None}
for name, deg in views.items():
if deg is None:
print(f"{name}: TODO")
continue
x, y, z = np.round(rot_y(deg) @ camera, 2) + 0.0
print(f"{name:<8} {deg:3d} deg -> 카메라 x={x:5.2f}, y={y:4.2f}, z={z:5.2f}")import numpy as np
def rot_y(deg):
r = np.radians(deg)
return np.array([[ np.cos(r), 0, np.sin(r)],
[ 0, 1, 0 ],
[-np.sin(r), 0, np.cos(r)]])
camera = np.array([0.0, 0.3, 2.0])
views = {"정면": 0, "오른쪽 측면": 90, "후면": 180, "왼쪽 측면": 270}
for name, deg in views.items():
x, y, z = np.round(rot_y(deg) @ camera, 2) + 0.0
print(f"{name:<8} {deg:3d} deg -> 카메라 x={x:5.2f}, y={y:4.2f}, z={z:5.2f}")후면(180°)에서 카메라의 z 가 −2.00 이 됩니다. 장비 뒤쪽 2 m 에서 바라보는 것입니다. 높이 y 는 회전해도 변하지 않습니다.
3D 장비 뷰를 이리저리 돌리다가 장비가 화면에서 사라졌습니다. 가장 먼저 할 일은?
- 브라우저를 닫고 컴퓨터를 다시 시작한다
- 상단 도구 막대의 뷰 초기화 버튼을 누른다
- Playground에서 ■ 정지를 누른다
- 명령 창에서 Ctrl+C 를 누른다
정답 보기
② — 뷰 초기화는 카메라를 정면 기본 시점으로 되돌립니다. ④는 로컬 서버를 꺼 버려 오히려 페이지가 동작하지 않게 되고, ③은 Python 실행만 멈춥니다.압력 값이 갑자기 0 bar 로 떨어졌습니다. 원인을 찾기 위해 먼저 볼 화면 영역은?
- A 3D 장비 뷰
- B 모듈 상태 패널
- C 실시간 크로마토그램
- D 이벤트 로그
정답 보기
④ — 상태 패널(B)은 "지금 값"을 보여 주어 이상을 알아차리게 하고, 이벤트 로그(D)는 펌프 꺼짐 · 오류 같은 사건을 시간순으로 남겨 "왜"를 알려 줍니다.자주 나는 오류와 해결#
1) 3D 뷰가 검은(또는 흰) 화면으로 비어 있음#
THREE.WebGLRenderer: Error creating WebGL context.- 원인: 브라우저의 하드웨어 가속(WebGL)이 꺼져 있거나, 그래픽 드라이버가 WebGL 을 막고 있습니다. 원격 데스크톱 · 가상 머신에서도 자주 생깁니다.
- 해결:
- Chrome: <b>설정 › 시스템 › 가능한 경우 그래픽 가속 사용</b>을 켜고 다시 시작합니다. Edge: <b>설정 › 시스템 및 성능 › 가능한 경우 그래픽 가속 사용</b>.
- 주소창에
chrome://gpu(Edge는edge://gpu)를 입력해 WebGL 항목이 "Hardware accelerated" 인지 확인합니다. - 그래도 안 되면 다른 PC나 브라우저에서 시도하고, 그동안은 Playground 실습으로 진행합니다.
2) 사이트를 고쳤거나 갱신했는데 화면이 예전 그대로이거나 일부가 빈 칸#
- 원인: 브라우저가 이전 파일을 캐시에서 불러왔습니다.
- 해결: <kbd>Ctrl</kbd>+<kbd>F5</kbd> 로 강력 새로고침합니다. 그래도 같으면 개발자 도구(<kbd>F12</kbd>)를 연 상태에서 새로고침 버튼을 길게 눌러(또는 우클릭) 캐시 비우기 및 강력 새로고침을 고릅니다.
3) python build.py --serve 실행 시 포트 오류#
OSError: [WinError 10048] 각 소켓 주소(프로토콜/네트워크 주소/포트)는 하나만 사용할 수 있습니다- 원인: 8000 번 포트를 이미 다른 프로그램(대개 앞서 켜 둔 같은 서버)이 쓰고 있습니다.
- 해결: 앞서 열어 둔 명령 창을 찾아 <kbd>Ctrl</kbd>+<kbd>C</kbd> 로 끄거나 창을 닫은 뒤 다시 실행합니다. 이미 서버가 켜져 있다면 새로 켤 필요 없이
http://localhost:8000으로 접속하면 됩니다.
4) 'python'은(는) 내부 또는 외부 명령 ... 이 아닙니다#
'python'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다.- 원인: Python 이 설치되지 않았거나 PATH 에 등록되지 않았습니다.
- 해결:
py --version이 되면py build.py --serve로 실행합니다. 안 되면 python.org 에서 Python 3 를 설치할 때 Add python.exe to PATH 에 체크합니다.
5) 페이지를 열었더니 3D 모델 · Playground 가 동작하지 않음 (주소가 file:///...)#
- 원인: HTML 파일을 탐색기에서 더블클릭해 열었습니다. 브라우저는
file://페이지에서 모델 파일 · Python 실행 환경을 불러오지 못하게 막습니다. - 해결: 로컬 서버를 켜고
http://localhost:8000으로 엽니다.
6) Playground 의 ▶ 실행 버튼이 계속 회색(눌리지 않음)#
- 원인: Python 실행 환경(Pyodide)을 아직 내려받는 중이거나, 인터넷이 막혀 내려받지 못했습니다.
- 해결: 수십 초 기다립니다. 계속 그대로면 인터넷 연결(학교 방화벽 포함)을 확인하고 <kbd>Ctrl</kbd>+<kbd>F5</kbd> 로 새로고침합니다.
과제 / 팀 미션#
아래 다섯 문제를 풀고, 각 답의 근거를 한 줄씩 적어 제출합니다.
- 펌프 바로 앞에 오는 장치는? ① 컬럼 ② 탈기 장치 ③ 검출기 ④ 오토샘플러
- 시료를 유로에 넣는 모듈의 시뮬레이터 기본 모델 번호는? ① G7111B ② G7114A ③ G7116A ④ G7129A
- 이동상의 아세토니트릴 비율을 높이면 역상 컬럼에서 일반적으로? ① 모든 피크가 빨리 나온다 ② 모든 피크가 늦게 나온다 ③ 피크 면적이 두 배가 된다 ④ 변화 없다
- 실물에서만 익힐 수 있는 것은? ① 유로 순서 ② 모듈 이름 ③ 피팅 누설을 눈과 손으로 찾기 ④ 크로마토그램 읽기
- 마우스 오른쪽 드래그의 3D 뷰 동작은? ① 회전 ② 평행 이동 ③ 확대/축소 ④ 정보 카드 열기
정답 보기
1-② 탈기 후 펌프로 들어갑니다. 2-④ G7129A 바이알 샘플러. 3-① 이동상이 덜 극성이 되어 물질이 정지상에서 더 쉽게 떨어집니다. 4-③ 누설 · 손 감각은 실물에서만 익힙니다. 5-② 왼쪽 = 회전, 휠 = 확대/축소입니다.참고자료#
- Agilent 1260 Infinity II LC 시스템 제품 페이지: https://www.agilent.com/en/product/liquid-chromatography/hplc-systems/analytical-hplc-systems/1260-infinity-ii-lc-system
- Agilent 1260 Infinity II 모듈별 사용자 설명서(User Manual) — Agilent 문헌 라이브러리에서 모델 번호(G7111B · G7129A · G7116A · G7114A)로 검색: https://www.agilent.com/en/support
- Agilent, The LC Handbook — Guide to LC Columns and Method Development (역상 크로마토그래피 · 머무름 개념): https://www.agilent.com/cs/library/primers/public/LC-Handbook-Complete-2.pdf
- Pyodide (브라우저 Python): https://pyodide.org/
- 이 사이트의 도구: Python Playground · 3D 뷰어 · 3D 시뮬레이터
- 다음 차시: ch02 1260 모듈과 배관 연결 이해