본문 바로가기

프로젝트(스마트 휴먼 라이프)

처음 해보는 서버 배포: React·Spring Boot 프로젝트를 Raspberry Pi에 올리기

2026.08.16 - [프로젝트(스마트 휴먼 라이프)] - AI를 ‘쓰는 것’에서 ‘잘 쓰는 것’으로: 소제동 인터랙티브 지도 개발기

 

AI를 ‘쓰는 것’에서 ‘잘 쓰는 것’으로: 소제동 인터랙티브 지도 개발기

그동안 무언가를 개발할 때나, 해야 할 때 AI를 편하게 쓰고는 있었지만 "잘" 쓰지 못하고 있다는 걸 깨달았다... 이제 프로젝트를 할 때는 기본적으로 뼈대를 잡고 어떻게 만들지 AI한테 더 숙지

devheo.tistory.com

 

이전 글과 이어서 작성해보겠다

 

먼저 프로젝트 기술 구성은 다음과 같은 모노레포 구조를 사용했다

 

여기서 모노레포란??

둘 이상의 연관된 프로젝트나 패키지 코드를
하나의 저장소(Repository)에서 통합해 관리하는 소프트웨어 개발 방식

 

soje-map/
├── app/                 # React 프론트엔드
├── public/              # 사진 및 정적 파일
├── backend/             # Spring Boot 백엔드
├── deployment/          # Raspberry Pi 및 Nginx 문서
├── Dockerfile           # 프론트엔드 이미지
├── docker-compose.yml
├── next.config.ts
└── package.json

 

사용한 기술은 다음과 같다

 

프론트엔드

  • React
  • TypeScript
  • Leaflet
  • OpenStreetMap
  • vinext/Vite

백엔드

  • Java 21
  • Spring Boot
  • Maven
  • Actuator 상태 확인 기능

배포

  • Raspberry Pi 5
  • Ubuntu Server
  • Docker
  • Docker Compose
  • 외부 Nginx 리버스 프록시
  • GitHub

현재 프론트엔드는 백엔드 API를 호출하지 않는다. Spring Boot는 추후 장소 정보 관리, 관리자 기능, 사진 업로드 등의 기능이 필요할 때 확장할 예정이다

 

4. 포트 구성

 

처음에는 프론트 개발 서버에서 3000 포트를 사용했다. 하지만 Raspberry Pi에서 이미 점유되어 있을 가능성이 있어 외부 공개 포트를 18080으로 변경했다.

 

프론트엔드
Raspberry Pi 18080 -> 컨테이너 3000

백엔드
Raspberry Pi 18081 -> 컨테이너 8080

 

 

백엔드는 아직 프론트에서 사용하지 않기 때문에 외부 네트워크에는 노출하지 않고 서버 내부에서만 접근할 수 있도록 했다

전체 요청 흐름은 다음과 같다

 

5. 로컬에서 Docker로 확인하기

 

Raspberry Pi에 바로 배포하지 않고 먼저 로컬에서 Docker 이미지가 정상적으로 실행되는지 확인했다

docker compose up -d

 

컨테이너 상태는 다음 명령으로 확인한다

docker compose ps

 

로그는 다음과 같이 확인할 수 있다

docker compmose logs --tail=100 frontend backend

 

프론트엔드 확인:

curl -I http://127/..

 

백엔드 상태 확인:

curl http://127.~/actuator/health

 

정상이라면 백엔드는 다음과 비슷한 응답을 반환한다

{
  "status": "UP"
}

 

테스트가 끝난 후에도 다음 명령으로 컨테이너를 종료했다

 

docker compose down

 

 

내가 Codex한테 배포하기전 요청한 것

이대로 라즈베리파이에 배포해보려고해. 라즈베리파이는 Ubuntu Server로 동작하고 있고, 이 안에 설치된 Docker에서 만든 작품을 배포하려고해. 이때, 사용한 3000 포트는 점유중일 수 있으니, 다른 포트로 변경 필요함. 결론적으로 raspberry pi에서 docker compose up -d 로 했을 때, 정상적으로 서버에 베포될 수 있도록 세팅까지 완료해야함. 완료된거 확인하면 git에 push하고, ubuntu server에 가서 pull로 다 가져오는 식으로 할게. commit하고 push 전상태까지 만들면 됨. git init은 진행.

 

 

6. GitHub에 소스 올리기

 

프로젝트 디렉터리에서 Git 저장소를 생성했다

 

git init
git add .
git commit -m "feat: add Soje memory map"

 

원격 저장소를 연결한 후 푸시한다

git remote add origin <GitiHub 저장소 주소>
git branch -M main
git push -u origin main

 

이수 수정사항은 다음과 같이 올린다

git add .
git commit -m "수정 내용"
git push origin main

 


 

7. Raspberry Pi 최초 배포

 

당연히 라즈베리파이 환경에서 작업해야 한다^^

Raspberry Pi에는 Ubuntu Server와 Docker가 설치되어 있다고 가정한다

 

먼저 시스템 아키텍처를 확인한다

uname -m

 

Raspberry Pi 5의 64비트 Ubuntu Server라면 일반적으로 다음 중 하나가 나온다

aarch64
arm64

 

GitHub 저장소를 내려받는다

docker compose ps
git clone <GitHub 저장소 주소> soje-map
cd soje-map

 

Docker Compose로 이미지를 빌드하고 실행한다

docker compose up -d

 

상태를 확인한다

 

프론트엔드를 Raspberry Pi 내부에서 직접 확인한다

curl -I http://127.~/

 

백엔드 상태를 확인한다

curl http://127.0.0.1:18081/actuator/health

 

8. 이후 소스 업데이트 방법

cd ~/soje-map
git pull --ff-only
docker compose build frontend
docker compose up -d --force-recreate --remove-orphans

 

 

Docker 캐시 문제를 확실히 배제해야 한다면 다음과 같이 프론트 이미지를 다시 빌드한다

docker compose build --no-cache frontend
docker compose up -d --force-recreate --remove-orphans

 

 

--ff-only를 사용하는 이유


--ff-only 는 Fast-forward가 가능한 경우에만 원격 변경사항을 가져오도록 하는 옵션이다

배포 서버에서는 직접 코드를 수정하지 않고 GitHub의 코드를 그대로 받아 사용하는 것이 일반적이다

 

저 명령을 사용하면 Raspberry Pi의 Git 기록과 GitHub 기록이 갈라진 경우 Git이 자동으로 병합하지 않고 중단한다

덕분에 배포 서버에 예상하지 못한 병합 커밋이 생기는 것을 방지할 수 있다

 

다음 장에서는 내가 배포를 하는 과정에서 겪었던 문제를 작성해보겠다