Raspberry Pi 위에서 돌아가는 카드 스와이프 & 여행코스 추천 서비스 – 도메인 설계와 배포 자동화
Raspberry Pi 환경에서 안정적인 배포를 위해 CI/CD + Docker + Nginx를 활용했다.
push → 자동 빌드, 테스트, Docker 이미지 생성main 브랜치 → Blue/Green 배포 트리거#root/.github/workflows/deploy.yml 샘플
name: FLIK CI/CD
on: [push]
env:
REGISTRY: ghcr.io
IMAGE_NAME: $
RASPBERRY_PI_HOST: $
RASPBERRY_PI_USER: $
MYSQL_ROOT_PASSWORD: $
MYSQL_DATABASE: $
#--생략--
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v3
with:
java-version: '21'
- name: Cache Gradle packages
uses: actions/cache@v4
- name: Test Flik Server
run: |
chmod +x gradlew
./gradlew clean test -Dspring.profiles.active=test --parallel --max-workers=4
build-and-deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
- name: Build with Gradle
run: ./gradlew clean build
- name: Build Docker image
run: docker build -t flik:latest .
- name: Push Docker image
run: docker tag flik:latest myregistry/flik:latest && docker push myregistry/flik:latest
upstream flik-app {
server flik-blue:8080 max_fails=3 fail_timeout=30s;
# server flik-green:8080 max_fails=3 fail_timeout=30s;
}
서비스코드를 작성한 후 테스트코드를 작성하는 것은 내키지 않는다.
테스트하면 코드가 다 꺠질것 같은 두려움 떄문일까. 이미 구현이 끝난 코드라고 생각하는 안일함 때문일까.
어떤쪽도 맞는 심리인 것 같다. 수정하기 귀찮고 다음 테스크로 빨리 넘어가고 싶으니…
하지만 테스트는 필요한 과정이다. 조잡하게 코드를 짜다보면 어디서 구멍이 날지 모른다.
그래서 나는 최대한 TDD를 도입하여 선 테스트코드 후 서비스코드 작성 전략을 취하고자 한다.
즉 요구기능 CRUD 구현은 단위 테스트부터 작성 -> 테스트가 모두 커버되도록 구현한다.
TDD 테스트 주도 개발(Test-Driven Development)
FLIK의 핵심 기능은 “스와이프 탐색 → 장소 카드 저장 → 코스 생성” 이다. 이를 기반으로 도메인을 설계하고, DDD 원칙을 준수했다.
| 도메인 | 역할 |
|---|---|
| Spot | 관광지 정보 저장, 카드 스와이프 기능과 연결 |
| Course | 사용자가 선택한 Spot들을 모아 코스 생성 |
| Auth | 로그인/회원 정보 관리, OAuth2 + JWT 인증 처리 |
| User | 사용자의 선호 장소 저장, 사용자의 카테고리별 선호벡터 저장 |
| Post | 게시글(여행기, 장소후기) |
Spot)public class Spot {
private final Long id;
private final String name;
private final String address;
private final SpotType type;
// 생성자
public Spot(Long id, String name, String address, SpotType type) {
this.id = id;
this.name = name;
this.address = address;
this.type = type;
}
// 행동 메서드
public void updateInfo(String newName, String newAddress) {
this.name = newName;
this.address = newAddress;
}
}
@Setter를 쓰지 않고 메서드 기반 업데이트FLIK API는 CQRS 패턴을 적용해 읽기/쓰기 요청을 분리하기로 했다.
POST /api/spots/save 요청 전달SaveSpotUseCase 호출Response.success()로 API 응답 반환이 서비스의 핵심은 사용자의 스와이프(장소 저장)이벤트이다.
‘스와이프’ 동작이 곧 요청이기 때문에 즉시 응답하고 비동기로 처리하는 로직을 구상했다.
따라서 장소 저장 요청은 이벤트 발행으로 이어진다.
Client → UseCase → EventListener → DB/Service
/api/spots/save 요청 전송SpotSwipeEvent 발행Response.success()isAlreadySaved 체크saveUserSpotupdateCategoryVector
Client
|
v
+-----------------+
| UseCase |
| (SpotSwipeEvent)|
+-----------------+
|
v
Response.success() <-- 이벤트 발행 직후 즉시 API 응답
|
v
+----------------------+
| EventListener (Async)|
+----------------------+
|
+--> check isAlreadySaved?
| |
| v
| already saved? ---+
| | |
| v |
| skip save |
| |
+--> saveUserSpot |
+--> updateCategoryVector |
|
v
Listener 처리 완료
Hexagonal + DDD 설계 도메인 중심 개발
“어디까지 추상화 해야하나?” 정답은 없다.
core와 port를 순회하며 도메인 객체가 어디로부터 어디로 흘러가는지 따라가다보니 역할분리의 효과를 알 수 있었다.
Raspberry Pi + Docker 배포로 실제 서버 환경을 체험
2편에서는 FLIK의 도메인 설계, API 흐름, 배포 자동화를 중심으로 살펴봤다. 다음 편에서는 데이터처리 (수집/정제/저장) 설계와 트러블슈팅을 다룰 예정이다.
🔜 3편 예고: “관광지 데이터 어떻게 요리할까”