본격적으로 들어가기 전에, 이 글을 어떻게 읽으면 좋을지 한마디 하고 싶습니다. 이 글은 명령어를 외우는 글이 아닙니다. '컨테이너가 왜 세상에 나왔고, 어떤 원리로 사람들을 편하게 해 주는가'를 그림으로 이해하는 글입니다. 그러니 중간에 나오는 짧은 코드 몇 줄이 안 외워져도 전혀 조급해하지 마세요. 원리만 잡으면 명령어는 나중에 저절로 따라옵니다. 저도 그렇게 배웠습니다.

여러분, 개발 좀 하다 보면 반드시 한 번은 듣거나 내뱉게 되는 전설의 문장이 있습니다. 바로 "어… 제 컴퓨터에선 되는데요?" 입니다. 내 노트북에서는 프로그램이 멀쩡하게 잘 돌아가는데, 그걸 회사 서버나 동료 컴퓨터에 올리면 갑자기 에러가 우수수 쏟아지는 상황. 저도 코딩을 늦게 시작한 사람으로서, 이 문장 때문에 밤새 머리를 쥐어뜯은 날이 정말 많았습니다.

원인은 대부분 시시합니다. 내 노트북엔 특정 프로그램 버전이 3.11인데 서버엔 3.9가 깔려 있다거나, 나는 어떤 라이브러리를 설치해 뒀는데 서버엔 그게 없다거나, 환경 설정 파일 한 줄이 다르다거나. 사람 잘못이라기보다는 '환경이 다르기 때문'입니다. 요리로 치면, 우리 집 부엌에선 완벽하게 만든 요리가 친구네 부엌에 가니 오븐 온도도 다르고 재료 브랜드도 달라서 맛이 이상해지는 거죠.

바로 이 고질병을 없애려고 나온 게 오늘의 주인공, 컨테이너(Container)도커(Docker)입니다. 어렵게 들리지만 겁먹지 마세요. 이 글을 다 읽고 나면 "아, 이거 결국 잘 포장한 도시락 이야기였네" 하고 웃으시게 될 겁니다.

저도 처음 '컨테이너'라는 단어를 들었을 때는 솔직히 벽이 느껴졌습니다. 개발 공부를 늦게 시작한 사람 입장에선 이런 신조어들이 자꾸 나오면 "아, 나는 역시 이쪽 사람이 아닌가 보다" 하고 주눅이 들거든요. 그런데 막상 뚜껑을 열어 보니, 컨테이너는 대단한 마법이 아니라 '짐을 잘 싸는 방법'에 대한 이야기였습니다. 우리 모두 이사할 때, 여행 짐을 쌀 때 이미 무의식적으로 해 오던 일이에요. 그걸 소프트웨어에 옮겨 놓은 것뿐입니다.

그래서 이 글은 명령어 암기 같은 건 최대한 뒤로 미루고, '왜 이게 생겼고, 대체 무슨 원리인가'를 일상 비유로 끝까지 풀어 드리는 데 집중하려 합니다. 원리를 그림으로 이해하면, 나중에 명령어는 필요할 때 검색해서 붙여 쓰면 그만이거든요. 그럼 천천히, 상자 하나 열어 보듯 시작해 볼까요?

컨테이너는 '레시피와 재료까지 통째로 담은 도시락'입니다. 요리(앱)만 주는 게 아니라, 그 요리를 만들 때 쓴 재료·양념·조리도구까지 상자 하나에 몽땅 넣어서 건네주는 거예요. 그러니 누가 어디서 열든 항상 똑같은 맛이 납니다.

1. 컨테이너가 대체 뭔가요?

제가 이사할 때를 떠올려 봅니다. 이삿짐을 옮길 때 그릇 따로, 수저 따로, 냄비 따로 아무렇게나 트럭에 던지면 어떻게 될까요? 도착해서 "어, 그 국자 어디 갔지?" "이 냄비 뚜껑은 어느 집 거야?" 하고 난리가 납니다. 그런데 '주방 세트'라고 이름 붙인 정리 상자 하나에 냄비·뚜껑·국자·양념을 한꺼번에 담아 두면? 새 집에 가서 그 상자만 열면 주방이 바로 세팅됩니다. 빠진 것도 없고요.

컨테이너가 바로 이 '이사용 정리 상자'입니다. 정식으로 말하면, 컨테이너는 앱(프로그램)과 그 앱을 실행하는 데 필요한 모든 것 — 런타임, 라이브러리, 설정, 환경 변수 — 을 하나의 상자에 담아 격리해 실행하는 기술입니다. 이 상자는 내 노트북에서 열든, 회사 서버에서 열든, 지구 반대편 클라우드에서 열든 항상 똑같이 동작합니다. 상자 안에 필요한 걸 다 넣어 뒀으니까요.

한 가지 더, 컨테이너를 여행 가방에 비유해도 좋습니다. 해외여행 갈 때 우리는 칫솔·충전기·옷·상비약을 캐리어 하나에 다 챙기죠. 왜냐면 도착지 호텔에 그게 다 있을지 모르니까요. 챙겨 가면 어느 나라 어느 호텔에 묵든 내 생활이 똑같이 굴러갑니다. 컨테이너도 마찬가지입니다. "혹시 서버에 이게 없으면 어쩌지?"를 고민할 필요 없이, 필요한 걸 전부 캐리어에 넣어 가니 어디에 내려놓아도 똑같이 돌아갑니다. 이 '내가 필요한 걸 다 데리고 다닌다'가 핵심 정서예요.

여기서 '해운 컨테이너' 비유가 왜 딱 맞는지 이야기하고 싶습니다. 옛날엔 배에 짐을 실을 때 자루, 나무 상자, 드럼통 등 제각각 모양으로 실었습니다. 항구마다 옮겨 싣는 게 지옥이었죠. 그런데 표준 규격의 쇠 상자(컨테이너)가 등장하자 세상이 바뀌었습니다. 안에 무엇이 들었든 상자 크기는 똑같으니, 크레인·트럭·배가 상자 내용물은 몰라도 표준 상자만 다루면 됐거든요. 소프트웨어 컨테이너도 똑같습니다. 안에 든 게 파이썬 앱이든 자바 앱이든, 바깥에서 다루는 방식은 표준화되어 있어서 어디서든 똑같이 실려 다닙니다.

조금 더 풀어 볼게요. 우리가 프로그램을 하나 만들면, 그 프로그램은 절대 혼자 돌지 못합니다. 파이썬 같은 런타임(엔진)이 있어야 하고, 남이 만들어 둔 라이브러리(부품)가 여러 개 필요하고, "이 폴더를 써라" "이 포트로 통신해라" 같은 설정도 맞아야 합니다. 즉 프로그램 하나가 제대로 돌려면 그 주변에 딸린 식구가 아주 많습니다. 예전엔 이 식구들을 서버마다 사람이 하나하나 챙겨 이사시켰습니다. 그러다 보니 하나만 빠져도 프로그램이 삐끗한 거예요. 컨테이너는 이 딸린 식구 전부를 한 상자에 넣어 통째로 이사시킵니다. 그러니 빠질 게 없습니다.

또 하나 중요한 성질이 '격리'입니다. 상자마다 벽이 있어서, A 앱이 쓰는 라이브러리 버전과 B 앱이 쓰는 버전이 서로 달라도 부딪히지 않습니다. 예전엔 서버 한 대에 앱을 여러 개 올리면 "쟤가 깔아 놓은 버전 때문에 내 앱이 죽었다" 같은 다툼이 잦았는데, 컨테이너는 각자 자기 방에서 자기 살림만 쓰니 그런 충돌이 사라집니다. 옆방 사람이 뭘 쓰든 내 방과는 상관없는 거죠.

💡 핵심 한 줄: 컨테이너는 "앱 + 실행에 필요한 모든 것"을 한 상자에 담아, 내 컴퓨터든 서버든 클라우드든 '어디서나 똑같이' 돌아가게 만드는 기술입니다. '환경이 달라서 생기는 문제'를 통째로 없애 줍니다.

2. 없을 때의 고통 — 왜 이게 나왔을까요

컨테이너가 없던 시절로 돌아가 볼게요. 새 서버에 프로그램을 올리려면 이런 과정을 거쳤습니다. "파이썬 이 버전 깔고, 라이브러리 A·B·C 설치하고, 환경 변수 설정하고, 폴더 권한 맞추고…" 이 설치 목록이 문서 몇 장에 걸쳐 있었고, 그중 한 줄만 빠져도 프로그램이 안 돌았습니다.

이 상황이 얼마나 답답했는지 비유로 옮겨 볼게요. 친구에게 우리 집 김치찌개 레시피를 알려 준다고 합시다. "김치 적당히, 물 좀, 불은 중간쯤"이라고 말로 전하면 친구가 끓인 찌개는 절대 우리 집 맛이 안 납니다. 재료 브랜드도 다르고, '적당히'의 기준도 다르니까요. 소프트웨어 설치 안내서가 딱 이랬습니다. 아무리 자세히 적어도 사람마다·서버마다 결과가 미묘하게 달라졌죠. 컨테이너는 여기서 "말로 설명하지 말고, 완성된 찌개를 통째로 보내자"로 문제를 우회한 겁니다.

더 무서운 건, 이 설치를 사람이 손으로 하다 보니 서버마다 미묘하게 달라진다는 점입니다. 개발 서버엔 라이브러리 버전이 1.2인데 운영 서버엔 실수로 1.1이 깔려 있고, 그래서 개발할 땐 잘 되던 게 진짜 서비스에선 터지고. "제 컴퓨터에선 되는데요"의 근본 원인이 바로 이 '환경 불일치'였습니다.

그래서 예전 개발자들 사이엔 "금요일엔 배포하지 마라"는 농담 반 진담 반의 규칙까지 있었습니다. 배포만 하면 무슨 일이 터질지 몰라서, 주말 앞두고는 무서워서 못 올렸던 거예요. 배포가 이렇게 겁나는 일이었다는 게 지금 생각하면 서글프죠. 컨테이너는 이 공포를 상당히 걷어냈습니다. 내가 확인한 상자를 그대로 옮기니, "옮기는 과정에서 뭔가 달라질까 봐" 떨 일이 줄었거든요.

제가 겪은 실제 장면 하나 들려드릴게요. 밤 열두 시, 내 노트북에선 완벽하게 돌던 앱을 서버에 올렸는데 자꾸 에러가 났습니다. 몇 시간을 뒤진 끝에 범인은 딱 한 글자였어요. 내 노트북 라이브러리는 소수점 버전이 하나 높았고, 서버 건 낮았던 겁니다. 잘못한 사람은 아무도 없었어요. 그냥 두 환경이 미묘하게 달랐을 뿐입니다. 이런 일이 하루 이틀이 아니었습니다. 배포할 때마다 매번 살얼음판을 걷는 기분이었죠.

컨테이너는 이 문제를 아주 단순한 발상으로 해결합니다. "설치 목록을 사람이 매번 따라 하지 말고, 완성된 상자를 통째로 복사해서 옮기자." 상자 안엔 이미 필요한 게 다 들어 있으니, 서버에서 새로 설치할 게 없습니다. 그러니 환경이 어긋날 일도 없죠.

비유하자면, 예전 방식은 이사 갈 때마다 새 집에서 가구를 처음부터 다시 사고 조립하는 것이었습니다. 설명서(설치 목록)를 보고 사람이 매번 손으로요. 그러니 지난번엔 있던 의자가 이번엔 빠지고, 나사 하나가 안 맞고 하는 사고가 났죠. 컨테이너 방식은 가구를 조립한 채로 방 하나를 통째로 들어 옮기는 것입니다. 새 집에 내려놓기만 하면 어제 쓰던 방 그대로예요. 조립할 일이 없으니 어긋날 일도 없습니다. 이 발상의 전환 하나가 배포 세계를 통째로 바꿔 놓았습니다.

여기서 오해 하나를 풀고 싶습니다. "그럼 컨테이너를 쓰면 코드를 안 짜도 되나요?" 아닙니다. 컨테이너는 코드를 대신 짜 주는 게 아니라, 내가 짠 코드를 '어떻게 포장해서 옮길까'를 해결해 주는 도구입니다. 요리사가 요리를 대신 해 주는 게 아니라, 완성한 요리를 안전하게 담아 배달할 도시락 통을 주는 거예요. 그러니 컨테이너를 배운다고 개발 자체가 쉬워지는 건 아니지만, '만든 걸 남에게 전달하는 고통'은 확실히 사라집니다. 그 고통이 생각보다 크기 때문에, 이 도구가 이렇게까지 사랑받는 겁니다.

3. 가상머신(VM)과는 뭐가 다른가요?

여기서 많은 분이 헷갈립니다. "그거 예전에 나온 가상머신(VM)이랑 같은 거 아니에요?" 비슷해 보이지만, 무게가 완전히 다릅니다. 비유로 풀어 볼게요.

가상머신(VM)'집 한 채를 통째로 새로 짓는 것'입니다. 프로그램 하나 돌리자고 그 안에 운영체제(OS)까지 통으로 새로 설치합니다. 화장실, 부엌, 보일러까지 다 갖춘 완전한 집이라 튼튼하고 완벽하게 격리되지만, 무겁고 느리고 자리를 많이 차지합니다. 집 한 채 짓는 데 시간이 걸리듯, 켜는 데도 몇 분씩 걸리죠.

컨테이너'한 건물 안의 방들'입니다. 건물의 뼈대·수도·전기(운영체제의 핵심인 커널)는 다 같이 공유하고, 각 방(컨테이너)은 자기 짐만 따로 둡니다. 집을 통으로 새로 짓지 않으니 가볍고, 방문 열듯 순식간에 켜집니다. 한 건물에 방을 수십 개 들일 수 있듯, 서버 한 대에 컨테이너를 아주 많이 올릴 수 있습니다.

구분가상머신(VM)컨테이너
비유집 한 채 통째로한 건물의 방들
운영체제(OS)상자마다 OS를 통째로 포함OS 커널을 공유(자기 짐만 따로)
무게무겁다(수 GB 단위)가볍다(상대적으로 작다)
켜지는 속도수십 초~수 분보통 몇 초 이내
한 서버에 올릴 수적다많다
격리 수준매우 강함(완전히 분리)충분히 강함(가볍게 분리)

왜 이 무게 차이가 나는지 조금만 더 들어가 볼게요. 컴퓨터의 운영체제(OS)는 크게 두 부분입니다. 커널이라는 핵심 엔진과, 그 위에 얹힌 각종 프로그램·설정입니다. VM은 이 커널까지 상자마다 새로 통째로 갖습니다. 그래서 무겁습니다. 반면 컨테이너는 커널은 호스트(바탕 컴퓨터)와 나눠 쓰고, 그 위 부분만 자기 것으로 챙깁니다. 건물의 기초·배관·전선은 공유하고 방 안 살림만 따로 두는 것과 똑같죠. 새로 지을 게 적으니 가볍고 빠를 수밖에요.

그럼 VM은 이제 필요 없느냐? 그건 아닙니다. 격리가 아주 강력해야 하거나, 아예 다른 종류의 운영체제를 돌려야 할 때는 VM이 여전히 제격입니다. 실제 현장에서는 VM이라는 큰 집 안에 컨테이너라는 방들을 여러 개 들여 함께 쓰는 경우가 아주 흔합니다. 둘은 경쟁 관계라기보다 층위가 다른 도구라고 보는 게 정확합니다.

정리하면, VM은 "완벽하지만 무거운 집", 컨테이너는 "가볍고 빠른 방"입니다. 둘 중 뭐가 낫다기보다 쓰임새가 다른데, 요즘 앱 배포에서 컨테이너가 대세가 된 건 이 가볍고 빠른 성질 덕분입니다. 서버 한 대를 알뜰하게 쪼개 쓰고, 필요하면 순식간에 개수를 늘렸다 줄였다 할 수 있으니까요.

한 장면으로 보는 차이

딱 한 장면으로 못을 박아 볼게요. 개발자 세 명이 각자 다른 노트북(윈도우·맥·리눅스)을 쓰는 팀이 있다고 합시다. 컨테이너가 없다면 각자 자기 컴퓨터에 맞춰 환경을 세팅하느라 셋의 환경이 미묘하게 다 다릅니다. "나는 되는데 너는 안 돼"가 반복되죠. 컨테이너가 있다면? 셋 다 똑같은 이미지를 받아 컨테이너로 켜니, 노트북 기종이 뭐든 안에서 돌아가는 환경은 완전히 동일합니다. 운영체제가 달라도 상자 안은 같은 셈이에요. 이 '기종을 안 가린다'는 점이 협업에서 어마어마한 평화를 가져다줍니다.

4. 도커(Docker)가 뭔가요?

지금까지 '컨테이너'라는 개념을 이야기했는데, 그 컨테이너를 실제로 만들고 옮기는 도구가 필요하겠죠? 그 대표 주자가 바로 도커(Docker)입니다. 이사 비유로 돌아가면, 컨테이너가 '정리 상자'라면 도커는 그 상자를 포장하고, 라벨 붙이고, 트럭에 실어 옮기는 이삿짐 센터인 셈입니다.

도커가 등장하기 전에도 컨테이너 비슷한 기술은 있었습니다. 하지만 다루기가 너무 어려워서 전문가들만 겨우 쓸 수 있었어요. 도커가 대단했던 건 이 어려운 기술을 누구나 쓸 수 있게 쉽게 포장했다는 점입니다. "레시피 한 장 쓰고, 명령어 한 줄 치면 상자가 뚝딱 만들어진다"로 문턱을 확 낮춘 거죠. 그래서 지금은 '컨테이너' 하면 자연스럽게 '도커'가 따라올 만큼 대명사가 됐습니다. 물론 도커 말고 다른 컨테이너 도구도 있지만, 입문자라면 도커 하나만 제대로 잡아도 충분합니다.

도커를 이해하려면 네 가지 단어만 알면 됩니다. 이미지, 컨테이너, Dockerfile, 레지스트리. 이름은 낯설지만 하나씩 비유로 풀면 정말 쉽습니다. 바로 다음 섹션에서 표로 정리해 드릴게요. "이 네 개가 서로 어떻게 이어지나"만 잡으면 도커의 90%는 이해한 겁니다. 반대로 이 네 개가 뒤엉키면 계속 헷갈리니, 여기서 확실히 못을 박고 가겠습니다.

💡 헷갈리지 마세요: '컨테이너'는 개념·기술이고, '도커'는 그 개념을 실제로 다루게 해 주는 대표 도구입니다. "컨테이너 = 상자, 도커 = 그 상자를 만들고 옮기는 장비" 이렇게만 기억하세요.

도커를 실제로는 어떤 순간에 쓰나요

개념만 들으면 "그래서 실무에서 언제 만지는 건데?" 싶으실 겁니다. 아주 일상적인 장면 몇 개로 그려 드릴게요. 첫째, 새 팀원이 합류한 날입니다. 예전엔 신입에게 "이거 깔고 저거 깔고…" 반나절짜리 설치 안내서를 건넸는데, 이제는 "이 상자 받아서 켜" 한 줄이면 개발 환경이 통째로 준비됩니다. 둘째, 내 앱을 서버에 올릴 때입니다. 서버에 뭘 설치하지 않고 상자만 옮겨 켜니 배포가 몇 분으로 줄어듭니다. 셋째, 데이터베이스 같은 걸 잠깐 띄워 테스트할 때입니다. 설치하고 지우고 하는 번거로움 없이 상자를 켰다 껐다 하면 되니, 내 컴퓨터가 지저분해지지 않습니다.

이렇게 보면 도커는 거창한 대규모 시스템에서만 쓰는 게 아니라, 혼자 공부하는 입문자에게도 당장 이득입니다. "내 컴퓨터를 어지럽히지 않고 이것저것 실험해 볼 수 있는 일회용 실험실"이 생기는 셈이니까요. 저도 새로운 도구를 테스트할 때 컨테이너부터 띄웁니다. 마음에 안 들면 상자째 버리면 그만이라 마음이 편하거든요.

5. 핵심 용어 4개 — 붕어빵으로 이해하기

제가 도커를 처음 배울 때 이 붕어빵 비유로 겨우 감을 잡았습니다. 붕어빵 틀 하나 있으면 똑같은 붕어빵을 몇 개든 찍어낼 수 있잖아요? 도커도 똑같습니다. 이 네 단어가 자꾸 뒤섞여서 초보를 괴롭히는데, 표 하나로 딱 정리해 두면 두고두고 든든합니다. 아래 표를 머릿속 서랍에 넣어 두세요.

용어일상 비유실제 의미
이미지(Image)붕어빵 / 설계도앱과 실행 환경을 담아 굳혀 둔 읽기전용 템플릿. 그 자체는 실행되지 않음
컨테이너(Container)틀로 찍어낸 붕어빵이미지를 실제로 실행한 인스턴스(살아 움직이는 프로그램)
Dockerfile붕어빵 레시피이미지를 어떻게 만들지 적어 둔 설명서(텍스트 파일)
레지스트리(Registry)이미지 창고 / 앱스토어완성한 이미지를 올리고 내려받는 저장소(예: Docker Hub)

흐름이 보이시나요? 레시피(Dockerfile)대로 틀(이미지)을 만들고, 그 틀로 붕어빵(컨테이너)을 찍어냅니다. 틀은 창고(레지스트리)에 보관했다가 필요할 때 꺼내 오고요. 이미지 하나로 컨테이너를 열 개든 백 개든 똑같이 찍어낼 수 있다는 게 핵심입니다. 붕어빵 틀 하나로 붕어빵을 무한히 만들 수 있듯이요.

이 네 단어를 한 문장으로 이어 보면 이렇습니다. "Dockerfile(레시피)로 이미지(틀)를 만들고, 이미지를 레지스트리(창고)에 보관했다가, 필요할 때 꺼내 컨테이너(붕어빵)로 실행한다." 이 한 문장이 입에 붙으면, 앞으로 어디서 '이미지'니 '레지스트리'니 하는 말을 들어도 당황하지 않습니다. 저는 이 문장을 포스트잇에 적어 모니터에 붙여 두고 며칠 봤더니 완전히 몸에 뱄어요. 여러분도 한번 소리 내어 읽어 보시길 권합니다.

이미지와 컨테이너, 왜 굳이 나눌까요?

"그냥 실행하면 되지 왜 틀이랑 붕어빵을 나눠요?" 좋은 질문입니다. 틀(이미지)은 한 번 잘 만들어 두면 변하지 않고, 그걸로 찍어낸 붕어빵(컨테이너)은 먹고 나서 버려도 틀은 그대로 남습니다. 즉 컨테이너 하나가 문제가 생기면 그냥 버리고 이미지로 새로 찍어내면 됩니다. 이 '언제든 똑같이 새로 찍어낼 수 있다'는 성질이 서비스 운영을 아주 편하게 만들어 줍니다.

이걸 현장에선 "소 말고 가축 떼처럼 다룬다"고 표현하기도 합니다. 무슨 말이냐면, 예전 서버는 애지중지 키우는 반려동물 같았습니다. 하나가 아프면(고장 나면) 밤새 병간호하듯 고쳐야 했죠. 그런데 컨테이너는 다릅니다. 하나가 이상하면 붙잡고 고치느라 애쓰지 말고, 그냥 버리고 이미지로 똑같은 걸 새로 하나 띄우면 끝입니다. 어차피 틀이 그대로 있으니 몇 초면 새 붕어빵이 나오니까요. 이 사고방식의 전환이 대규모 서비스를 안정적으로 굴리는 비결입니다.

또 하나, 이미지에는 재미있는 성질이 있습니다. 붕어빵 틀을 만들 때 여러 겹(레이어)을 차곡차곡 쌓아 만든다는 점입니다. "기본 파이썬 상자" 위에 "내 라이브러리 층"을 얹고, 그 위에 "내 코드 층"을 얹는 식이죠. 그래서 코드 한 줄만 바꿨을 때는 아래 공통 층은 재사용하고 맨 위 얇은 층만 새로 굽습니다. 덕분에 두 번째 굽기부터는 훨씬 빨라집니다. 지금 당장 깊이 알 필요는 없지만, "이미지는 층으로 쌓인다"는 감만 있어도 나중에 도움이 됩니다.

레지스트리는 왜 '앱스토어'에 비유할까요

레지스트리를 '창고'라고 했지만, 저는 '앱스토어' 비유를 더 좋아합니다. 스마트폰 앱스토어에 가면 남들이 만든 앱을 검색해서 바로 내려받아 쓸 수 있죠? 레지스트리도 똑같습니다. 대표적인 공개 레지스트리인 Docker Hub에 가면, 전 세계 사람들이 잘 만들어 올려 둔 데이터베이스·웹서버·프로그래밍 언어 상자가 잔뜩 있습니다. 내가 데이터베이스를 직접 설치할 필요 없이, 그 이미지를 pull로 내려받아 run으로 켜기만 하면 데이터베이스 하나가 몇 초 만에 돕니다. 이게 얼마나 편한지는 예전에 데이터베이스를 손으로 설치하다 반나절을 날려 본 사람만 압니다.

반대로 내가 만든 이미지를 창고에 올려 두면, 우리 팀 누구나·어느 서버든 그 창고에서 똑같은 상자를 받아 쓸 수 있습니다. 회사 내부용으로 쓰는 비공개(프라이빗) 레지스트리도 있어서, 외부에 공개하지 않고 팀 안에서만 이미지를 주고받을 수도 있습니다. 정리하면 레지스트리는 '이미지를 올리고 내려받는 공용 창고 겸 앱스토어'입니다.

6. 전체 흐름 한눈에 보기

말로만 하면 아직 안개 속이죠. 도커로 앱을 배포하는 전체 여정을 그림으로 그려 보겠습니다. 딱 다섯 단계입니다.

Dockerfile
레시피 작성
build
이미지 생성
push
창고에 올리기
pull
서버로 내려받기
run
컨테이너 실행
1

Dockerfile 작성

"이 앱은 이렇게 포장하세요"라는 레시피를 텍스트 파일로 씁니다.

2

build로 이미지 생성

레시피대로 재료를 담아 붕어빵 틀(이미지)을 굽습니다. docker build

3

레지스트리에 push

완성한 틀을 창고(Docker Hub 등)에 올려 둡니다. docker push

4

서버에서 pull

배포할 서버가 창고에서 틀을 그대로 내려받습니다. docker pull

5

run으로 컨테이너 실행

내려받은 틀로 붕어빵(컨테이너)을 찍어 실행합니다. docker run

이 흐름의 아름다운 점은, 내가 만든 틀을 서버가 '그대로' 내려받아 돌린다는 겁니다. 서버에서 뭘 새로 설치하지 않으니 "제 컴퓨터에선 되는데" 사태가 원천 차단됩니다. 내 컴퓨터에서 구운 붕어빵 틀이 서버에서도 똑같은 붕어빵을 뽑아내니까요.

여기서 레지스트리(창고)의 역할을 한 번 더 짚고 싶습니다. 왜 굳이 창고를 거칠까요? 내 컴퓨터에서 서버로 파일을 바로 복사하면 안 되나 싶지만, 창고를 두면 여러모로 편합니다. 첫째, 여러 서버가 같은 창고에서 똑같은 틀을 내려받으니 열 대든 백 대든 완전히 동일한 앱이 깔립니다. 둘째, 예전 버전 틀도 창고에 남아 있어서 문제가 생기면 이전 버전으로 되돌리기(롤백)가 쉽습니다. 셋째, 창고에는 남들이 잘 만들어 둔 공개 이미지도 잔뜩 있어서, 데이터베이스나 웹서버 같은 걸 직접 설치하지 않고 내려받아 바로 쓸 수 있습니다. 앱스토어에서 앱 내려받듯이요.

참고로 이 다섯 단계 중 build → push까지는 개발자(또는 자동화 벨트)가 하고, pull → run은 서버 쪽에서 일어납니다. 나중에 이 뒷부분을 사람이 아니라 자동화가 대신하게 만드는 게 바로 CI/CD인데, 이 이야기는 뒤에서 다시 이어가겠습니다.

7. 아주 쉬운 Dockerfile 예시

이제 실제 레시피를 살짝 구경해 볼까요? 겁먹지 마세요. 딱 네 줄만 보겠습니다. 각 줄이 요리 레시피의 한 문장이라고 생각하면 됩니다.

💡 아래는 개념 이해용 예시입니다. 한 줄도 못 외워도 괜찮아요. "아, 이런 문장들이 모여 상자를 만드는구나" 정도만 느끼시면 충분합니다.

FROM python:3.11
"기본 재료로 파이썬 3.11이 깔린 상자를 가져와라." 맨바닥부터 만들지 않고, 남이 잘 만들어 둔 기본 상자 위에서 시작합니다. 요리로 치면 '밑국물이 준비된 냄비'를 가져오는 거예요.

COPY . /app
"내 프로젝트 파일들을 상자 안 /app 폴더에 복사해 넣어라." 내가 만든 코드를 상자 안으로 담는 단계입니다. 이삿짐을 상자에 넣는 거죠.

RUN pip install -r requirements.txt
"상자 안에서 필요한 라이브러리들을 설치해라." 앱이 돌아가는 데 필요한 부품(라이브러리)을 상자 안에 미리 다 깔아 둡니다. 이게 있어서 서버는 아무것도 따로 설치할 필요가 없어요.

CMD ["python", "app.py"]
"이 상자를 열면 app.py를 실행해라." 상자를 켰을 때 무엇을 할지 정해 주는 '시동 버튼'입니다. 도시락 뚜껑을 열면 바로 먹을 수 있게 세팅해 두는 거죠.

혹시 각 줄 앞의 대문자 단어(FROM·COPY·RUN·CMD)가 부담스러우신가요? 이건 그냥 "이 줄은 이런 종류의 지시야"라고 알려 주는 이름표일 뿐입니다. 편지 봉투에 '받는 사람' '보내는 사람'이라고 칸이 나뉜 것과 같아요. FROM 칸엔 기본 상자를, COPY 칸엔 옮길 짐을, RUN 칸엔 설치할 것을, CMD 칸엔 켤 때 할 일을 적는다. 이렇게 칸 채우기라고 생각하면 훨씬 만만해집니다. 실제로 대부분의 간단한 앱은 이 네 칸을 채우는 것만으로 상자가 완성됩니다.

이 레시피 파일을 두고 명령어 두 개만 알면 됩니다.

docker build -t 내앱 .
"이 레시피대로 '내앱'이라는 이름의 틀(이미지)을 구워라."

docker run 내앱
"그 틀로 붕어빵(컨테이너)을 찍어 실행해라."

딱 이 두 줄입니다. build로 굽고, run으로 켠다. 자전거로 치면 페달 밟고 앞으로 나가는 정도의 기본기예요. 물론 실무에서는 "몇 번 포트로 연결해라" "이 폴더를 바깥과 공유해라" 같은 옵션이 뒤에 붙지만, 그건 필요할 때 하나씩 익히면 됩니다. 지금은 "레시피 쓰고 → build로 굽고 → run으로 켠다"는 세 박자만 손에 쥐고 가세요. 이 세 박자가 도커 실무의 8할입니다. 나머지 2할은 이 위에 옵션을 얹는 것뿐이에요.

어떤가요? 생각보다 문장이 사람 말에 가깝죠. FROM(기본 상자 가져오기) → COPY(내 짐 넣기) → RUN(부품 설치) → CMD(시동 걸기). 이 네 박자만 기억하면 Dockerfile의 뼈대는 다 이해한 겁니다. 실제로는 더 다양한 명령이 있지만, 세부 옵션은 공식 문서에서 필요할 때 찾아보면 되니 지금 다 외울 필요 없습니다.

여기서 초보가 가장 신기해하는 부분을 짚어 드릴게요. 바로 첫 줄 FROM입니다. "아니, 맨바닥부터 안 만들고 남이 만든 상자를 가져와도 돼요?" 네, 그게 정상이고 오히려 권장됩니다. 세상 모든 파이썬 개발자가 파이썬 설치를 매번 처음부터 할 필요는 없잖아요. 누군가 잘 만들어 레지스트리에 올려 둔 '파이썬이 깔린 기본 상자'를 밑바탕으로 삼고, 나는 그 위에 내 것만 얹으면 됩니다. 이 '남의 어깨 위에서 시작하기'가 컨테이너를 이렇게까지 빠르고 편하게 만든 핵심입니다.

그리고 한 가지 마음의 짐을 덜어 드리고 싶어요. 이 Dockerfile은 한 번 잘 써 두면 계속 재사용하는 파일입니다. 매번 새로 짜는 게 아니에요. 처음에 조금 고생해서 레시피 한 장을 완성해 두면, 그다음부터는 docker build 한 줄이면 똑같은 상자가 몇 번이고 뚝딱 나옵니다. 처음의 수고가 두고두고 나를 편하게 해 주는 구조인 거죠. 그러니 첫 레시피 앞에서 너무 겁먹지 마세요.

8. 컨테이너가 주는 이점과 대가

세상에 공짜는 없죠. 컨테이너도 좋은 점이 압도적으로 많지만, 배울 게 있다는 대가는 따릅니다. 솔직하게 양쪽 다 보여드릴게요.

👍 좋은 점

  • 환경 일치: 내 컴퓨터·서버·클라우드에서 항상 똑같이 동작. "제 컴퓨터에선 되는데" 소멸
  • 빠른 실행: VM보다 훨씬 가벼워 몇 초 만에 켜짐
  • 이식성: 상자 하나만 옮기면 어디서든 실행. 클라우드를 바꿔도 그대로
  • 확장이 쉬움: 트래픽 몰리면 같은 붕어빵을 여러 개 찍어 나눠 처리
  • 깔끔한 격리: 앱마다 상자가 달라서 서로 간섭하지 않음

👎 대가·주의점

  • 학습곡선: 새 개념·명령어를 익히는 초반 진입 장벽
  • 이미지 관리: 이미지가 쌓이면 용량과 버전 관리를 신경 써야 함
  • 데이터 보관 개념: 컨테이너는 버려질 수 있어 데이터 저장 위치를 따로 챙겨야 함
  • 보안 관리: 상자 안에 비밀정보를 잘못 넣지 않도록 주의 필요

제 경험상 학습곡선은 처음 며칠이 고비고, 그 산만 넘으면 오히려 "예전엔 이 고생을 왜 손으로 했지?" 싶어집니다. 겁먹지 마세요. 지금 이 글을 읽는 것만으로도 그 산의 절반은 넘은 겁니다.

'이식성'도 한 번 더 짚고 싶은 이점입니다. 이식성이란 '옮겨 심어도 잘 자란다'는 뜻이에요. 컨테이너로 포장해 두면, 오늘은 내 노트북에서 돌리다가 내일은 회사 서버로, 다음 달엔 다른 클라우드로 옮겨도 상자는 그대로 따라갑니다. 특정 회사나 특정 서버에 묶이지 않는 거죠. 이게 왜 중요하냐면, 사업이 커지거나 상황이 바뀌어 인프라를 갈아탈 때 앱을 처음부터 다시 세팅하는 지옥을 피할 수 있기 때문입니다. "우리는 이 클라우드 아니면 못 옮겨요" 하는 족쇄에서 자유로워진다는 건 생각보다 큰 힘입니다.

이점 중에서도 '확장이 쉽다'는 부분을 조금 더 이야기하고 싶습니다. 명절에 붕어빵 가게 앞에 손님이 갑자기 몰렸다고 상상해 보세요. 붕어빵 틀이 하나뿐이면 줄이 끝없이 길어집니다. 그런데 똑같은 틀로 순식간에 틀을 여러 개 더 놓고 동시에 굽기 시작하면 줄이 쭉쭉 줄겠죠. 컨테이너가 바로 이걸 합니다. 트래픽이 몰리면 같은 이미지로 컨테이너를 여러 개 더 띄워 부하를 나누고, 손님이 빠지면 다시 줄입니다. 이걸 자동으로 해 주는 게 나중에 나올 쿠버네티스고요. 예전 방식으로는 서버 한 대를 새로 세팅하는 데 한나절이 걸렸는데, 컨테이너는 몇 초면 되니 차원이 다릅니다.

반대로 대가도 솔직히 인정합시다. 새로운 개념과 명령어를 익혀야 하고, 이미지가 자꾸 쌓이면 디스크가 꽉 차기도 하며, "이 서비스가 지금 어떤 버전 이미지로 돌고 있더라?" 하는 관리 부담도 생깁니다. 하지만 이건 대부분 습관과 약간의 정리 규칙으로 해결되는 문제입니다. 뒤의 '초보 실수' 섹션에서 이 부분의 실전 팁을 따로 챙겨 드릴게요.

9. CI/CD·클라우드와 어떻게 이어지나요

컨테이너는 혼자 쓰기보다 다른 도구들과 짝을 이룰 때 진짜 힘을 발휘합니다. 큰 그림에서 어디쯤 위치하는지 짚어 드릴게요.

제가 컨테이너를 이해하고 나서 가장 좋았던 건, 그동안 파편처럼 흩어져 있던 개발 용어들이 하나의 지도 위에서 이어지기 시작했다는 점이었습니다. GitHub, 배포, 클라우드, 자동화… 따로따로 어렵게만 느껴지던 것들이 "결국 상자를 만들어 어딘가로 옮기는 이야기"라는 큰 줄기로 꿰어졌거든요. 그 연결 고리를 짚어 드릴게요.

먼저 CI/CD와의 궁합입니다. 코드를 고칠 때마다 자동으로 검사하고 배포하는 자동화 벨트가 CI/CD인데요, 이 벨트의 결과물(빌드 산출물)을 컨테이너 이미지로 만들어 배포하면 궁합이 환상적입니다. "자동으로 붕어빵 틀을 굽고, 자동으로 서버에 실어 나른다"가 되는 거죠. 이 이야기는 CI/CD 이야기에서 자세히 풀었으니 함께 보시면 그림이 완성됩니다.

조금 더 구체적으로 그려 볼게요. 예전엔 코드를 고치면 이런 순서였습니다. 개발자가 손으로 테스트하고, 손으로 서버에 접속해, 손으로 설치하고, 손으로 껐다 켜고. 사람 손이 여러 번 들어가니 실수가 끼기 쉬웠죠. 이제는 코드를 저장소에 올리는 순간 자동화 벨트가 알아서 테스트 → 이미지 굽기(build) → 창고에 올리기(push) → 서버가 내려받아 실행(pull·run)까지 쭉 돌립니다. 사람은 코드만 올리고 커피 한 잔 하면 배포가 끝나 있는 거예요. 이 매끄러운 자동화의 중심에 '컨테이너 이미지'라는 표준 산출물이 있습니다. 벨트가 나르는 물건이 항상 똑같은 규격의 상자라서 자동화가 깔끔하게 맞물리는 거죠.

다음은 규모의 문제입니다. 붕어빵 몇 개는 손으로 관리해도 되지만, 컨테이너가 수십·수백 개로 늘어나면 어느 게 죽었는지, 트래픽 따라 몇 개를 더 켜야 하는지 사람이 일일이 못 챙깁니다. 이 수많은 컨테이너를 자동으로 지휘하는 지휘자가 바로 쿠버네티스(Kubernetes)입니다. 이건 다음 글 쿠버네티스 이야기에서 이어집니다.

마지막으로 클라우드 배포. 예전에 AWS EC2(클라우드 월세방)에 앱을 직접 올려 본 분이라면, 그 서버 위에 컨테이너를 올리면 배포가 훨씬 깔끔해진다는 걸 금방 체감하실 겁니다. 예전엔 월세방(EC2)에 들어가서 파이썬 깔고, 라이브러리 깔고, 설정 맞추느라 반나절을 썼다면, 이제는 이미 짐이 다 싸인 상자 하나를 방에 내려놓고 켜기만 하면 되니까요. 게다가 방을 옮기거나(다른 서버로 이전), 아예 이사 업체를 바꿔도(다른 클라우드로 이전) 상자는 그대로 가져가면 됩니다. 특정 클라우드에 발목 잡히지 않는 이 자유로움이 컨테이너의 큰 매력입니다.

이 모든 흐름을 한 장에 꿰고 싶다면 DevOps 시리즈 개요부터 훑어보시길 권합니다. 컨테이너는 그 큰 지도 위의 한 조각이라, 앞뒤 맥락을 함께 보면 "아, 이래서 이게 필요했구나"가 훨씬 선명해집니다. 순서에 정답은 없지만, 저는 개인적으로 컨테이너 → CI/CD → 쿠버네티스 순으로 익히는 걸 추천합니다. 상자를 만들 줄 알아야, 그 상자를 자동으로 나르고(CI/CD), 여러 상자를 지휘하는(쿠버네티스) 이야기가 자연스럽게 이어지거든요.

10. 초보가 흔히 하는 실수 & 해결

저도 초반에 다 겪은 실수들입니다. 미리 알면 피할 수 있으니, 부끄럼 없이 공유합니다. 이런 실수는 창피한 게 아니라 거의 모든 입문자가 통과하는 관문이에요. 오히려 미리 알고 조심하면 몇 시간, 며칠의 삽질을 아낄 수 있으니 남는 장사입니다.

👎 흔한 실수

  • 이미지에 비밀키 넣기: 비밀번호·API 키를 상자 안에 그대로 굽기
  • 너무 큰 이미지: 안 쓰는 것까지 다 담아 상자가 비대해짐
  • 데이터를 컨테이너 안에만 두기: 컨테이너를 지우면 데이터도 함께 증발
  • latest 태그만 쓰기: 버전 표시 없이 늘 '최신'만 써서 어떤 게 돌아가는지 헷갈림

👍 해결책

  • 비밀정보는 이미지 밖 환경 변수·비밀관리 도구로 주입
  • 가벼운 기본 이미지 사용, 필요한 것만 담아 상자를 슬림하게
  • 중요 데이터는 볼륨(외부 저장 공간)에 따로 보관해 컨테이너와 분리
  • 이미지에 버전 태그를 명확히 붙여 무엇이 돌아가는지 관리

특히 세 번째 '데이터 증발'은 초보가 정말 많이 당합니다. 컨테이너는 언제든 버리고 새로 찍어낼 수 있다고 했죠? 그 말은 컨테이너 안에 저장한 것도 함께 사라진다는 뜻입니다. 그래서 사용자 데이터처럼 남아야 하는 건 반드시 상자 밖 저장 공간(볼륨)에 둬야 합니다. "붕어빵은 버려도 되지만, 손님 주문서는 따로 보관한다" 이렇게 기억하세요.

첫 번째 '비밀키 넣기'도 뼈아픈 실수라 다시 강조합니다. 편하다고 비밀번호나 API 키를 Dockerfile이나 이미지 안에 그대로 넣어 버리면, 그 이미지를 창고(레지스트리)에 올리는 순간 비밀정보가 통째로 남에게 노출될 수 있습니다. 상자를 여는 사람이면 누구나 안에 든 열쇠를 볼 수 있는 셈이죠. 그래서 비밀정보는 상자 안에 굽지 말고, 실행할 때 바깥에서 살짝 넣어 주는 방식(환경 변수·비밀관리 도구)을 씁니다. "도시락에 통장 비밀번호를 써 붙이지 않는다" 정도로 기억하시면 됩니다.

그리고 두 번째 '너무 큰 이미지'는 초반엔 티가 안 나다가 나중에 발목을 잡습니다. 안 쓰는 파일까지 다 담으면 상자가 무거워져서, 창고에 올리고 내리는 데 시간이 오래 걸리고 저장 공간도 잡아먹습니다. 해법은 간단합니다. 가벼운 기본 이미지를 고르고, 정말 필요한 것만 담는 습관이에요. 이사할 때 안 쓰는 짐은 과감히 버리는 것과 같습니다. 이 세 가지(비밀키·큰 이미지·데이터 증발)만 조심해도 초보 시절 사고의 대부분은 피해 갑니다.

11. 자주 묻는 질문 (FAQ)

제가 강의나 스터디에서 컨테이너를 설명하면 늘 비슷한 질문이 돌아옵니다. 여러분도 지금 머릿속에 물음표가 떠 있을 테니, 가장 많이 나온 다섯 개를 골라 정리해 드릴게요.

Q. 컨테이너랑 도커는 같은 말인가요?
A. 아니요. '컨테이너'는 기술·개념(상자)이고, '도커'는 그 컨테이너를 만들고 다루는 대표 도구(장비)입니다. 도커 말고 다른 도구도 있지만, 입문자는 도커부터 익히면 충분합니다.

Q. 비개발자인데 도커를 꼭 배워야 하나요?
A. 당장 직접 다루지 않아도, "컨테이너로 배포한다"는 말이 나올 때 무슨 뜻인지 아는 것만으로 협업이 훨씬 수월해집니다. 이 글 수준의 이해면 대화에 낄 수 있어요. 깊게 파는 건 필요할 때 해도 늦지 않습니다.

Q. 이미지랑 컨테이너, 매번 헷갈려요.
A. 붕어빵으로 기억하세요. 이미지 = 틀(안 변함), 컨테이너 = 틀로 찍어낸 붕어빵(실행 중, 버려도 됨). 틀 하나로 붕어빵을 여러 개 찍어낼 수 있다는 것만 잡으면 됩니다.

Q. 가상머신을 이미 쓰고 있는데 컨테이너로 갈아타야 하나요?
A. 꼭 그럴 필요는 없습니다. 둘은 쓰임새가 다르고, 실제로는 VM 위에 컨테이너를 올려 함께 쓰는 경우도 많습니다. 다만 앱을 가볍고 빠르게 배포·확장하고 싶다면 컨테이너가 큰 도움이 됩니다.

Q. 컨테이너를 쓰면 정말 "제 컴퓨터에선 되는데" 문제가 완전히 사라지나요?
A. '환경 차이'에서 비롯되는 문제는 거의 사라진다고 봐도 됩니다. 상자를 통째로 옮기니까요. 다만 상자 바깥의 요소 — 예를 들어 연결하는 데이터베이스 주소가 다르거나, 상자 밖에서 넣어 주는 설정값이 서버마다 다르면 — 여전히 차이가 날 수 있습니다. 그래서 '설정은 상자 밖에서 주입한다'는 원칙을 지키는 게 중요합니다.

Q. 도커를 배우면 그다음엔 뭘 배우나요?
A. 컨테이너가 여러 개로 늘어나면 자동으로 관리해 주는 쿠버네티스로, 배포를 자동화하려면 CI/CD로 이어집니다. 큰 지도는 DevOps 시리즈 개요에 있습니다.

12. 마무리 체크리스트

여기까지 오시느라 정말 수고 많으셨습니다. 낯선 단어가 참 많았죠? 그런데 돌아보면 우리가 이야기한 건 전부 '짐을 잘 싸서, 어디서든 똑같이 풀어 쓰는 법'이었습니다. 이사 상자, 도시락, 여행 가방, 붕어빵 틀. 전부 여러분이 이미 아는 일상의 물건들이었어요. 어려운 건 단어였지, 개념이 아니었습니다.

오늘 이야기를 한 문장으로 줄이면 이렇습니다. "컨테이너는 앱과 실행 환경을 통째로 담은 상자라서, 어디서 열든 똑같이 돌아간다." 이 한 줄만 몸에 배어도 여러분은 이미 '제 컴퓨터에선 되는데' 병에서 벗어난 겁니다. 떠나기 전에 아래 항목을 스스로 점검해 보세요.

혹시 이 글을 읽으며 "그래서 나는 지금 뭘 해 보면 되지?" 궁금하신 분을 위해 아주 작은 첫걸음을 남겨 둡니다. 거창하게 배포부터 할 필요 없어요. 내 컴퓨터에 도커를 설치하고, Docker Hub에서 남이 만든 가벼운 이미지 하나를 pull로 받아 run으로 켜 보는 것. 딱 그것만 해 봐도 "아, 상자가 이렇게 뜨는구나"가 손끝으로 느껴집니다. 그다음에 이 글의 네 줄짜리 Dockerfile을 흉내 내 내 작은 앱을 상자에 담아 보면 됩니다. 한 번에 다 하려 하지 말고, 상자 하나 열어 보기부터. 그거면 충분합니다. 작은 성공 하나가 다음 한 걸음을 끌어 주거든요.

정말 잘 오셨습니다. 처음 이 단어들을 봤을 때의 막막함, 저도 똑똑히 기억합니다. 그런데 지금 여러분은 붕어빵 틀 하나로 도커의 절반을 설명할 수 있게 됐어요. 별거 아니죠? 이게 계단 하나 오른 겁니다.

마지막으로 격려 하나 더 남길게요. 새 기술 앞에서 "다들 아는데 나만 모르는 것 같다"는 기분, 저도 정말 자주 느꼈습니다. 그런데 사실 대부분의 사람도 처음엔 다 몰랐어요. 붕어빵 틀과 도시락 비유로 컨테이너를 남에게 설명할 수 있다면, 여러분은 이미 '아는 사람' 축에 든 겁니다. 오늘 배운 걸 옆 사람에게 한 번 설명해 보세요. 설명이 되면 진짜 내 것이 된 거니까요.

다음 글에서는 컨테이너가 수십, 수백 개로 불어났을 때 이걸 자동으로 지휘하는 쿠버네티스 이야기를 들고 오겠습니다. "붕어빵 장수 한 명이 전국에 매장을 둔 프랜차이즈 사장이 되는 법"이라고 생각하시면 딱 맞습니다. 상자 하나를 잘 만드는 법을 익힌 지금이, 그 여러 상자를 지휘하는 이야기로 넘어가기에 딱 좋은 때입니다. 그때 또 뵙겠습니다. 천천히, 그러나 확실히 가시죠.

(2026년 8월 기준 작성 — 화면·명령어·정책은 바뀔 수 있으니 도커 공식 문서를 함께 확인하세요.)

「코딩 몰라도 AI로 사주앱 만들기」기획부터 AWS 배포·도메인 연결까지 손잡고 가는 237쪽 실전 가이드
교보 → YES24 →
← 기술 블로그 목록으로