노션클론 리팩토링 (18) - 포트폴리오를 어떻게 실행할까?(Docker편)

김영현·2025년 1월 24일
0

서론

노션클론 프로젝트는 프로그래머스 데브코스에서 바닐라JS를 이용해 프론트엔드를 맛볼수 있는 과제였다.
그런데, 하고싶은 공부도하고 포트폴리오로 활용할 수 있지않을까 싶어 조금씩 손댔던게 이렇게 변했다.

  1. Vanilla JS를 이용한 상태기반 렌더링 방식
  2. Redux를 공부하며 Redux와 유사한 방식의 전역 상태관리
  3. 클라이언트측 라우팅
  4. node.js의 http모듈을 이용한 웹서버
  5. node.js + mongodb(node driver)를 이용한 api서버

이제 프로젝트를 종료하고 외부(음...면접관 님들?)에서 확인할 수 있게 하고 싶었다.
이를 편하게 실행할 수 있는 방법은 대략 3가지 정도 같다.

  1. 클라우드 이용
  2. 홈 서버 공개
  3. Docker

각 방법의 장단점을 살펴보자.

클라우드 이용시

  • 장점 : 항상 켜져있다, 클라우드 지식까지 같이 얻을 수 있다.
  • 단점 : 비용, 약하지만 존재하는 러닝커브

클라우드는 항상 비용이 문제다. 이번 프로젝트는 DB까지 사용하여 DB서버에 대한 비용도 염두해야한다.
클라우드마다 무료 버전이 존재하지만, 기간이 짧은(3개월)이 대부분이다.
취준기간이 길어질 것을 감안하여...그나마 기간이 제일 길고 학습해두면 좋은 AWS가 최선의 선택지 같다.

결론 : 클라우드 이용시 AWS 사용

홈 서버

홈 서버는 구축해두면 두고두고 쓸모 있을 것 같다.
다만 보안이라던가, 전기세(비용), 노트북 배터리 수명 등 다양한 마이너스 요인이 존재한다.

일단 보류

Docker

도커는 비용도 나가지않고, 어떤 환경에서든 도커를 실행할수만 있다면 프로젝트를 공개하기 좋다.
다만 사용자가 도커를 다운받아야한다는 게 흠이다. 공개된 주소로 접속할 수 있는게 아니니까...
이 역시 배워둔다면 유익한 기술 중 하나다.

결론

Docker를 이용해서 프로젝트를 실행할 수 있게 만든다. + AWS 프리티어를 이용해 서버를 공개해본다.
아예 둘 다 공부해보자


Docker

도커란 컨테이너라고하는 느슨하게 격리된 환경에서 애플리케이션을 패캐징-실행 할수 있는 기능을 제공하는 소프트웨어다.
한 컴퓨터 내에서 각기 다른 환경을 구현할 수 있게 도와준다.

여기서 말하는 컨테이너 란 무엇일까?

컨테이너


컨테이너는 코드와 모든 종속성을 패키지화하여 애플리케이션이 한 컴퓨팅 환경에서 다른 컴퓨팅 환경으로 빠르고 안정적으로 실행될 수 있도록 하는 표준 소프트웨어 단위입니다. Docker 컨테이너 이미지는 애플리케이션을 실행하는 데 필요한 모든 것을 포함하는 가볍고 독립형 실행 가능한 소프트웨어 패키지입니다.
출처 : https://www.docker.com/resources/what-container/

코드덩어리와 종속성을 한 데 묶어, 어디서든 실행 가능하게 만든다.


출처 : https://www.docker.com/resources/what-container/

컨테이너와 VM은 유사하지만 다른 개념이다.

  • VM : 하나의 서버를 여러 서버로 바꾸는 물리적 하드웨어의 추상화다. 각기 다른 GuestOS를 가진다. AWS에서 제공해주는 EC2도 VM이다.
  • 컨테이너 : 다른 컨테이너와 OS커널을 공유할 수 있다

즉 컨테이너란 하나의 독립된 프로세스다.

이미지

컨테이너가 독립된 프로세스라면 파일, 설정등을 가져올 곳이 필요하다. 그게 바로 컨테이너 이미지다.
컨테이너 이미지는 컨테이너를 실행하는데 필요한 구성을 표준화한 패키지다.

DockerHub에 접속해보면 공개되어있는 공식 컨테이너 이미지들이 있다.

한번 노드 이미지를 받아보자.

먼저 https://docs.docker.com/desktop/setup/install/windows-install/ 링크에 들어가 설치하라는대로 윈도우용 도커를 설치해주자. (다른 운영체제버전도 찾아보면 있다)

해당 명령어를 터미널에 입력해주면...


이렇게 이미지가 설치된다.

설치된 이미지를 보는 방법은 위처럼 GUI를 이용해도되지만, docker image ls명령어를 입력해도 볼 수 있다.

설치된 node이미지를 실행해보자. 이미지를 실행하는 명령어는 아래와 같다.

docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]

다양한 옵션을 줄 수 있다. 이렇게 이미지를 실행하면 새로운 컨테이너를 만들고 그 내부에서 이미지가 실행된다.

넣은 옵션들을 살펴보자.

  • -it : interaction의 준말로 컨테이너 내부와 대화를 하겠다는 의미다. 즉, 컨테이너 내부 명령어를 이용할 수 있다.
  • node : 이미지 이름이다.
  • bash : 컨테이너 내부와 대화할때 bash를 사용하겠다는 의미다. 그래서 root계정으로 접속되어있다.

실제로 노드 이미지가 잘 작동하는지 체크하기위해 node명령어를 입력해본다.

잘 작동한다.

위처럼 도커 이미지는 도커 컨테이너를 쉽게 실행하기 위한 패키지다. 공개되어있는 이미지를 다운받아 쓸 수도 있지만, 현재 프로젝트를 도커 이미지로 만들면 좋을 것 같다.

그러기 위해선 DockerFile에 대하여 알아야한다.

DockerFile

도커파일은 이미지를 빌드하기위한 명령어 모음이라고 보면 된다. 이전에 사용했던 노드 이미지 또한 DockerFile을 작성해서 빌드한다.

DockerFile을 작성하는 방법은 일반적으로 다음과 같다.

  • FROM <image> : 이미지의 기준이 되는 것을 정한다.
  • RUN <command> : 현재 이미지 위의 새 레이어에서 모든 명령을 실행하고 결과를 커밋한다.
  • WORKDIR <directory> : 컨테이너의 작업 디렉토리를 설정한다.
  • COPY <src> <dest> : 호스트 src경로에 있는 파일을 컨테이너의dest경로로 복사한다.
  • CMD <command> : 컨테이너가 실행될때 자동으로 실행될 명령어를 지정해준다.
  • EXPOSE <port> : 어떤 포트를 외부에 공개할지 설정한다.

현재 프로젝트의 폴더 구성은 다음과 같다.

- root/
  - web-server/...
  - api-server/...

각 폴더마다 dockerfile을 작성해보자. 참고로 웹서버는 3000, api서버는 4000포트를 갖는다.

웹서버의 도커파일

# Use Node.js as the base image
FROM node:20.9.0

# Set the working directory
WORKDIR /web-server

# Copy only necessary files
COPY package*.json ./
RUN npm install

# Copy the rest of the server files
COPY . .

# Expose the port the web server runs on
EXPOSE 3000

# Start the web server
CMD ["npm", "start"]

api서버의 도커파일

# Use Node.js as the base image
FROM node:20.9.0

# Set the working directory
WORKDIR /api-server

# Copy only necessary files
COPY package*.json ./
RUN npm install

# Copy the rest of the server files
COPY . .

# Expose the port the API server runs on
EXPOSE 4000

# Start the API server
CMD ["npm", "start"]

약간 다르지만 아주 유사한 도커파일을 두개 작성했다.
이제 DB를 연결하고 각 도커파일을 실행하면 된다. 이때 필요한게 docker-compose다.

docker-compose

Docker Compose는 단일 파일로 다중 컨테이너를 정의하고 관리할 수 있다. DB를 세팅하고, 각 도커파일을 실행하려는 목적과 부합한다.
언어는 YAML를 사용한다. YAML은 github actions에서도 사용했으므로 어색하진 않다.

version: "1.0"
services:
  mongodb:
    image: mongo:8.0.4
    container_name: mongodb
    ports:
      - "27017:27017"
    volumes:
      - mongo_data:/data/db
  api-server:
    build:
      context: ./api-server
    ports:
      - "4000:4000"
    volumes:
      - ./api-server:/api-server
      - /api-server/node_modules
    depends_on:
      - mongodb
    environment:
      - MONGODB_URI=mongodb://mongodb:27017
  web-server:
    build:
      context: ./web-server
    ports:
      - "3000:3000"
    volumes:
      - ./web-server:/web-server
      - /web-server/node_modules
    depends_on:
      - api-server

volumes:
  mongo_data:
  • services : 실행할 각 서비스를 정의한다.

    vscode에 docker익스텐션을 설치했더니 각 서비스를 실행할 수 있는 버튼을 보여준다.
  • volumes : 실제 호스트의 저장장치와 도커 컨테이너를 연동한다. 데이터를 영구저장하기 위함이다.
    • api-server, web-server같은 경우 로컬에서 수정된 파일을 실시간으로 반영하기 위함이다.
  • depends_on : 서비스간 종속성을 관리한다. DB서버가 가동되고 나서 API서버가 구동되어야 하므로 위와같이 정의했다.

이렇게 작성된 YAML을 프로젝트 루트폴더에 docker-compose.yml로 저장하고 명령어를 이용하여 빌드를 실행해보자.

docker-compose up --build

해당 명령어를 실행하게되면 주루루루룩하면서 각 컨테이너가 실행된다.

컨테이너와 네트워크

기존 API서버에서 mongodb의 URI는 다음과 같았다.

const URI = 'mongodb://localhost:27017';

이 URI를 아무런 수정없이 이용했었는데, 자꾸 API서버가 mongodb와 연결하는 과정에서 죽었다.
곰곰히 생각해보니 API서버의 localhost는 해당 컨테이너의 localhost를 가리킨다.


이미지 출처 : https://shawn-dev.oopy.io/463a30bf-bf32-44e4-88be-8b4722e5549a

그렇다면 다른 컨테이너의 주소를 어떻게 가리킬 수 있을까?
만약 docker-compose를 이용한다면 docker-compose.yml에서 정의한 서비스 명을 사용하면 된다.


mongodb가 해당 컨테이너의 주소가 된다.
따라서 아래 주소로 db를 접속해주면 된다.

const URI = 'mongodb://mongodb:27017';

다음편에서는 AWS를 이용해 배포해보겠습니다.

profile
모르는 것을 모른다고 하기

0개의 댓글