포스트

구구단으로 코랩을 붙였다 — 에러 두 개가 더 남았다

구구단으로 코랩을 붙였다 — 에러 두 개가 더 남았다

구구단 프로그램을 만들려다 코랩 환경을 붙이게 됐다. 정작 오늘 남은 건 구구단이 아니라 에러 두 개다. 둘 다 “내가 뭘 잘못 알고 있었는지”를 정확히 짚어줬다.


코랩을 VS Code에 붙이는 공식 방법이 있었다

처음엔 안 되는 줄 알았다. 검색하면 나오는 건 대부분 반대 방향 — 코랩 화면을 쓰고 계산은 내 노트북이 하는 방식(jupyter_http_over_ws)이다. 내가 원한 건 반대였다. VS Code에서 코드를 쓰고, 계산은 코랩이 하는 것.

찾아보니 구글이 공식 확장을 냈다. 2025년 11월 13일에 나온 Colab VS Code 확장이다.

1
확장 설치 → 노트북 열기 → Select Kernel → Colab → Auto Connect → 구글 로그인

터널링이나 SSH 우회 같은 건 필요 없다. 예전 글들이 알려주는 우회법을 그대로 따라 했으면 시간만 버렸을 것이다. “안 된다”는 정보에는 날짜를 확인해야 한다는 걸 다시 배웠다.


에러 1 — 컴퓨터가 두 대라는 걸 잊었다

드라이브의 이미지를 불러오려는데 VS Code에 창이 떴다.

1
EROFS: read-only file system, mkdir '/content'

마운트는 잘 된 것 같은데 왜 못 찾지? 한참 헤맸다.

원인은 코랩과 아무 상관이 없었다. /content는 코랩 서버에만 있는 폴더인데, 내가 그 경로를 VS Code에게 열라고 시킨 것이다. VS Code는 자기가 사는 맥북에서 찾았고, 없으니까 만들려 했고, 맥OS가 거부했다. 맥OS는 카탈리나 이후 루트(/)가 읽기 전용이다.

터미널에서 직접 해보면 똑같은 에러가 난다.

1
2
3
4
5
$ mkdir /content
mkdir: /content: Read-only file system

$ mount | grep "on / "
/dev/disk3s1s1 on / (apfs, sealed, local, read-only, journaled)

원격 커널을 쓴다는 건 컴퓨터가 두 대가 된다는 뜻이다.

1
2
3
[ 내 맥북 ]  화면·파일        [ 코랩 서버 ]  계산
 VS Code                       파이썬 커널
 /Users/...                     /content/drive/...

여기서 제일 쓸모 있었던 건 에러가 어느 쪽에서 났는지 구분하는 법이다.

에러가 뜬 위치낸 주체
VS Code 모달 창내 맥북
셀 아래 빨간 트레이스백코랩 서버

모달 창이 떴다는 건 파이썬이 실행조차 안 됐다는 뜻이다. 셀 실행 실패가 아니다. 이걸 알고 나니 “마운트를 다시 해볼까” 하는 헛수고를 멈출 수 있었다.

경로에도 함정이 두 개 있었다.

1
2
내 드라이브\catimg\catimg.jpg              ← 화면에 보이는 이름
/content/drive/MyDrive/catimg/catimg.jpg   ← 코랩이 아는 실제 경로

내 드라이브의 실제 이름은 MyDrive(띄어쓰기 없음)다. 웹 화면의 한글 이름은 별명이고, 폴더의 본명은 따로 있다. 그리고 코랩은 리눅스라 구분자가 \가 아니라 /다.


에러 2 — 확장자는 이름표일 뿐이었다

이미지를 리사이즈해서 저장하려는데 이번엔 셀 안에서 터졌다.

1
OSError: cannot write mode RGBA as JPEG

.jpg 파일을 읽어서 .jpg로 저장하는데 왜 안 되지? 파일 정보를 찍어보고 이해했다.

1
2
3
4
5
형식(format) : WEBP          ← 확장자는 .jpg인데 실제 내용은 WEBP
모드(mode)   : RGBA          ← 투명도 채널이 있다
shape : (550, 513, 4)        ← 마지막 4가 채널 수
맨 왼쪽 위 픽셀 : [72 112 75 0]
                           └─ 이 0이 알파. 완전 투명

확장자는 사람이 붙인 이름표일 뿐이고, 실제 형식은 파일 내용에 적혀 있다. 웹에서 저장한 이미지가 이런 경우가 많다. file 명령으로 확인하면 바로 나온다.

1
2
$ file catimg.jpg
catimg.jpg: RIFF (little-endian) data, Web/P image

PIL은 이름표를 무시하고 내용을 읽으니 여는 건 잘 됐다. 문제는 저장할 때다. 저장할 땐 이름표(.jpg)를 보고 JPEG으로 쓰려 하는데, JPEG에는 투명도를 담을 칸이 없다.

4칸짜리 짐을 3칸 트렁크에 넣으려는 상황이다. 이미지는 R·G·B·A 4채널인데 JPEG 트렁크에는 R·G·B 3칸만 있다. 이때 PIL은 알파를 조용히 버리지 않고 멈춰 선다. 불편하지만 말없이 데이터를 버리는 것보다 낫다.

여기서 한 번 더 틀릴 뻔했다

convert('RGB')로 3채널로 바꾸면 되겠다고 생각했다. 되긴 되는데 투명했던 부분이 검정으로 깔린다. 직접 찍어서 비교했다.

1
2
convert('RGB')  투명영역 → [0, 0, 0]        검정
흰배경 합성      투명영역 → [255, 255, 255]  흰색

배경이 투명한 고양이 컷아웃이었으니, 그냥 변환했으면 배경이 새까맣게 나왔을 것이다. 제대로 하려면 흰 도화지를 깔고 그 위에 얹어야 한다. 알파를 마스크로 쓰는 것이다.

1
2
canvas = Image.new('RGB', image.size, (255, 255, 255))
canvas.paste(image, mask=image.split()[-1])   # 알파를 마스크로

투명도를 살리고 싶으면 아예 PNG로 저장하면 된다. 형식을 바꿔서 4칸을 확보하는 쪽이다.


왜 구구단으로 시작한 게 좋았나

돌아보면 구구단은 소재였고, 실제로 얻은 건 환경이었다.

구구단은 GPU도, 외부 라이브러리도, 데이터도 필요 없다. 그래서 에러가 나면 원인이 하나로 좁혀진다. 환경 문제다.

만약 처음부터 ResNet 학습 코드로 시작했다면, 에러가 났을 때 이 질문에 답할 수 없었을 것이다. 커널이 안 붙었나? 라이브러리가 없나? 경로가 틀렸나? 모델 코드가 틀렸나? 변수가 네 개라 어디를 봐야 할지 모른다.

Hello World가 존재하는 이유가 이것이었다. 실패할 수 없을 만큼 단순한 코드를 먼저 돌려서, 이후에 나는 모든 에러를 “내 코드 문제”로 좁히는 장치. 의사가 복잡한 검사 전에 체온부터 재는 것과 같다.


정리

오늘 한 일을 머신러닝 파이프라인에 놓으면 이렇게 된다.

1
2
[환경 구축] → [데이터 접근] → [데이터 검증] → [전처리] → [모델 학습] → [평가]
   완료          완료            완료          완료       ← 다음

아직 불을 켜지 않았다. 여기까지는 주방 준비다.

그리고 다음 단계로 갈 때 걸릴 게 이미 보인다. 이미지가 4채널이라 그대로 모델에 넣으면 expected 3 channels but got 4로 터진다. .convert('RGB')를 잊지 말 것.

배운 것 세 줄로 줄이면 이렇다.

  1. “안 된다”는 정보에는 날짜를 확인한다. 공식 방법이 생겼을 수 있다.
  2. 에러가 뜬 위치가 범인을 알려준다. 모달 창인가, 트레이스백인가.
  3. 확장자를 믿지 말고 format과 mode를 찍어본다. 데이터는 내 가정과 다르다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.