Docker로 React 어플리케이션 실행하기

정다빈·2023년 6월 25일
3
post-thumbnail

최근 키즈노트 FE개발파트는 도커(Docker) 도입을 검토하고 있어요. 사실 도커의 도 모르는 상태인데요, 🥺 도입 후 도커랑 어색한 사이이면 안 되겠죠?
이번 포스트에서는 도커에 대해 간단히 알아보고, 도커를 이용해서 리액트 어플리케이션을 실행해 보도록 할게요.

🐳 도커란 무엇인가요?

도커 이전에 존재했던 가상 머신(Virtual Machine)

가상 머신은 소프트웨어를 통해 생성된 가상의 컴퓨터 환경이에요.
가상 머신은 실제 컴퓨터 시스템과 유사한 환경을 제공하기 때문에, 가상 환경에서 별도의 OS와 어플리케이션을 실행할 수 있어요.

가상 머신은 위 그림처럼 하이퍼바이저(Hypervisor)라는 소프트웨어에 의해 관리돼요. 하이퍼바이저는 물리적 하드웨어의 자원을 가상화하여 여러 가상 머신이 자원을 공유하면서도 독립적으로 실행될 수 있도록 합니다.
(물리적 하드웨어의 자원을 가상화한다는 것은 CPU, 메모리 등의 물리적인 자원을 소프트웨어를 통해 논리적인 자원으로 변환하는 것을 의미해요.)

실제로 가상 머신을 실행한 모습은 아래와 같습니다.

가상 머신의 장점

  • 하나의 물리 서버 위에서 여러 서버 역할을 실행해 자원의 효율을 높일 수 있어요.
  • 서로 다른 환경을 설정하고 테스트할 수 있어요.
  • 가상 머신은 서로 독립적이어서 하나의 가상 머신에 문제가 발생하더라도 다른 가상머신에 영향을 주지 않아요.

가상 머신의 단점

  • 가상 머신의 수에 따라 물리적 하드웨어보다 성능이 나빠질 수 있어요.
  • 하이퍼바이저가 자원 할당을 관리해야 하므로, 오버헤드가 발생할 수 있어요. 특히 작업들이 CPU로 집중되거나, 고속의 I/O 작업에서 성능 저하가 두드러질 수 있어요.

도커의 등장

도커는 가상 머신과 마찬가지로 하나의 물리 서버 위에서 여러 독립적인 환경을 실행하고, 가상 머신의 단점이었던 성능 문제를 보완하고, 환경 의존성과 설정 문제로 인해 흔하게 발생했던 배포 오류를 해결하기 위해 등장했어요.

도커는 하이퍼바이저, 게스트 OS를 설치하지 않는 대신 도커 엔진 위에서 독립된 환경인 컨테이너(Container)를 만들고 관리해요.

Dockerfile, Image, Container

Dockerfile
Dockerfile은 어플리케이션을 이미지로 빌드 하기 위한 설정 파일이에요. 이 파일에는 어플리케이션 실행에 필요한 OS, 패키지, 설정과 명령어를 작성해요.
Dockerfile을 기반으로 이미지를 만들고, 이 이미지를 실행하여 컨테이너를 생성할 수 있어요.

아래는 Dockerfile의 예시입니다.

빌드를 실행하면 명령어들이 순차적으로 실행되면서 각각의 이미지 레이어가 생성돼요. 이렇게 만들어진 레이어들은 스택 구조로 쌓이게 됩니다.
빌더는 이전 빌드에서 만들어진 레이어를 재사용하려고 시도하는데요, 만약 레이어가 변경되지 않았다면 캐싱 된 레이어를 가져오지만 레이어가 변경되었다면 해당 레이어부터 이후의 모든 레이어들을 다시 빌드 하게 됩니다.

Image
이미지는 Dockerfile을 바탕으로 만들어진 실행 환경의 스냅샷이에요. 이미지는 컨테이너가 실행될 수 있는 기본 환경을 제공해요. 필요한 라이브러리, 설정 등이 포함되어 있어 하나의 이미지로 여러 개의 컨테이너를 만들 수 있어요.
이미지는 Docker Hub와 같은 공개 레지스트리에 공유할 수 있어요.

Container
컨테이너는 실제로 실행되는 어플리케이션 인스턴스에요. 컨테이너는 어플리케이션 실행에 필요한 모든 종속성을 포함해서 독립적인 환경을 제공해요.
컨테이너는 다른 어플리케이션이나 프로세스를 충돌 없이 한 시스템에서 동시에 실행할 수 있어요.

🎣 도커를 직접 사용해 봅시다!

1. 도커 설치하기

다운로드 페이지에서 도커를 설치해 줄게요. 설치가 완료된 후 도커 앱을 실행하면 아래와 같은 화면을 볼 수 있어요.

2. 리액트 어플리케이션 생성하기

Create React App을 이용하여 간단한 리액트 어플리케이션을 생성해 주었어요. 어플리케이션을 실행하면 localhost:3000에서 아래와 같은 화면이 나타나요.

3. 이미지 작성하기

루트 디렉토리에 아래와 같이 Dockerfile을 생성해줄게요.

  • FROM : 디폴트 이미지를 설정할 때 사용하며, DockerfileFROM 명령어로 시작해야 해요. Docker Hub에서 이미지를 가져와서 사용하는 것이 가장 쉽고 일반적이에요.
    저는 리액트 어플리케이션을 실행시켜줄 것이기 때문에, Node.js의 가장 안정적인 버전인 18버전의 slim을 사용했어요. Node.js 이미지 문서를 확인해 보면, slim은 Node.js를 실행하는데 필요한 최소한의 패키지만 포함되어 있다고 설명하고 있어요.
  • COPY : COPY <src> <dest> 문법을 사용하며, 호스트 컴퓨터의 <src> 파일 또는 디렉토리를 → 컨테이너의 <dest>로 복사해요.
    저는 어플리케이션의 모든 파일을 컨테이너에 넣어주고 싶기 때문에, 경로를 . .로 지정해 주었어요. 추후 단계에서 컨테이너를 실행시켜보면, 컨테이너의 루트 디렉토리에 모든 파일들이 옮겨진 것을 확인할 수 있어요.

  • RUN : 이미지를 빌드 하는 과정에서 실행되기 때문에, 일반적으로 어플리케이션 실행에 필요한 라이브러리를 이 단계에서 설치해요.
    저는 npm install로 패키지들을 설치해 줄게요.
  • CMD : 컨테이너를 최초로 실행할 때 사용되는 명령어입니다. Dockerfile에서는 하나의 CMD 명령어를 가질 수 있으며, 둘 이상의 CMD 명령어가 있는 경우 마지막 명령어만 실행돼요.
    저는 리액트 어플리케이션을 실행시키기 위해 npm start를 사용했어요.
  • EXPOSE : 컨테이너가 실행될 때 사용할 포트 번호를 명시해요. EXPOSE 명령어는 포트 번호를 명시하는 단순한 문서 역할을 하기 때문에, 실제 컨테이너 실행에는 영향을 주지 않아요.
    저는 3000번 포트를 사용하겠다고 명시해 주었어요.

4. 이미지 빌드하기

아래 명령어를 이용해서 이미지를 빌드해 줄게요.

docker build -t simple-react-docker-practice .

-t 옵션을 이용해서 이미지에 simple-react-docker-practice라는 태그를 붙어주었어요.

터미널에서 명령어들이 실행되는 것을 확인할 수 있어요. FROM 명령어에 적었던 Node.js는 캐시를 사용했다는 것을 알 수 있네요!

5. 컨테이너 실행하기

아래 명령어를 이용해서 컨테이너를 실행해 줄게요.

docker run -dp 127.0.0.1:3000:3000 simple-react-docker-practice
  • -d : 컨테이너를 백그라운드에서 실행시킬 때 사용하는 옵션이에요. 컨테이너를 백그라운드에서 실행시킨다면 컨테이너의 실행에 필요한 터미널을 계속 열어둘 필요가 없기 때문에 보통 백그라운드에서 실행합니다.
  • -p : 호스트와 컨테이너 간의 포트 연결을 위해 사용하는 옵션이에요. 호스트 주소:컨테이너 포트 번호 형식을 사용하며, 저는 호스트의 localhost:3000과 컨테이너의 3000 포트를 연결해 주었어요. 이러한 매핑 옵션이 없다면 호스트에서 컨테이너의 어플리케이션에 접근을 할 수 없습니다.
    마지막에는 빌드 단계에서 지정했던 이미지의 태그를 넣어주었어요.

터미널에서 리액트 어플리케이션을 종료한 후, http://localhost:3000 으로 접속하면 컨테이너에서 어플리케이션이 실행 중인 것을 확인할 수 있습니다.

6. 코드 수정하기

이번엔 App.tsx 파일에서 코드를 살짝 수정해 볼게요.

7. 컨테이너 재실행하기

"4. 이미지 빌드하기", "5. 컨테이너 실행하기" 단계와 동일하게 컨테이너를 실행시켰더니, 아래와 같이 에러가 발생했어요.

이전 컨테이너가 호스트 3000번 포트를 사용하고 있기 때문에 또다시 3000번 포트로 실행이 불가능한 상태에요. 이를 해결하기 위해서는 이전 컨테이너를 제거해 주어야 합니다.

아래 명령어를 이용해서 현재 실행 중인 컨테이너 목록을 조회해 줄게요.

docker ps

3000번 포트를 사용 중인 컨테이너 아이디를 확인한 후, 컨테이너를 중지시켜 줍니다.

docker stop <container-id>

컨테이너가 중지되었다면 아래 명령어를 이용해서 컨테이너를 제거할 수 있어요.

docker rm <container-id>

다시 컨테이너를 실행한 후 localhost:3000으로 접근하면 정상적으로 실행 중인 것을 확인할 수 있어요. 수정한 대로 잘 나오고 있네요!👍🏻

"1. 도커 설치하기" 단계에서 설치했던 도커 앱에서도 컨테이너의 상태를 확인할 수 있어요.

🐟 마무리

지금까지는 도커에 대해 잘 몰랐기 때문에 굉장히 어색한 사이였는데요, 이제는 1px 정도 친해진 기분이 들어요.
배포 단계에서 환경 설정 문제로 배포에 실패하는 케이스를 간혹 보았는데요, 도커를 도입해서 잘 사용하면 배포 실패 확률을 많이 줄일 수 있지 않을까 하는 기대가 듭니다. 앞으로도 도커와 친해지고 싶어요!

실습에 사용된 코드는 여기서 확인할 수 있습니다.

📚 Reference

profile
Frontend Developer

2개의 댓글

comment-user-thumbnail
2024년 10월 23일

안녕하세요~ 멋진 포스팅을 보기 위해 미래에서 왔습니다. 🚀

1개의 답글