- 2025.09.26 ~ 2025.10.20 (4주)
- 총 6명 (백엔드 6명)
곧감(GoatGam)은 음식 배달 백엔드 서비스로 Spring Boot 기반의 백엔드 서버로 설계되었습니다. 설계 과정에서 배달 음식 주문 과정의 불편함을 줄이고, 사용자에게 직관적이고 간결한 주문 경험을 제공하는 것을 목표로 프로젝트를 진행 하였습니다.
곳감은 이메일을 통한 가입을 지원하며 가입 시 Customer, Owner 등 사용자 유형을 선택하여 가입할 수 있습니다.
'Owner' 사용자의 경우 자신의 가게를 등록하고 여러 메뉴와 주문 옵션 등을 등록할 수 있고, 'Customer' 사용자의 주문을 수락, 취소, 환불 하는 등 자신의 가게의 주문을 관리할 수 있습니다. 이 프로젝트는 사용자의 주소를 기준으로 배달 가능한 음식점을 탐색하고, 메뉴 선택부터 주문, 결제까지의 전 과정을 통합하여 관리합니다. 'Customer' 사용자는 자신의 주소를 등록하고 메뉴를 선택하여 주문할 수 있고, 주문을 취소할 수 있습니다. 또한, 'Customer' 사용자가 주문을 넣으면 배달 진행상황에 따라 'Owner' 사용자는 배달 진행상황을 업데이트 할 수 있습니다.
'Master' 사용자의 경우 'Manager'를 지정하고 관리할 수 있으며 지정된 'Manager'는 'Customer'과 'Owner' 사용자를 관리하고 사용자의 주문을 관리할 수 있습니다. 또한, 삭제된 식당을 다시 등록하거나 삭제된 사용자를 다시 가입시켜주는 등 삭제된 데이터에 대한 관리 기능을 수행할 수 있습니다.
- Swagger (로컬용)
- Swagger (배포용)
MVP 기능 구현 장바구니 주문 내역 공통예외 처리 핸들러 그 외기능 검증 및 보완 |
식당식당 API 개발 테스트 코드 작성 카트 테스트 공통코드 리뷰 |
팔로우 & 리뷰 팔로우 API 개발 리뷰 API 개발 ERD 연관관계 정의 테스트테스트 코드 작성 |
식당식당 API 개발 테스트 코드 작성 공통예외 처리 문서화 |
인증/인가JWT 인증 구현 Spring Security 인프라AWS EC2/RDS/IAM 주소/지역 관리 |
음식/옵션음식, 옵션 API 개발 주문처리주문처리 구현 AIAI서비스 구현 결제결제 시스템 구현 배포Docker GitHub Actions |
| Category | Stack |
|---|---|
| Language | |
| IDE | |
| Framework | |
| Build Tool | |
| Database | |
| Library | |
| API | |
| DevOps | |
| Tools |
📣기술 & 라이브러리 선정 이유
1️⃣ Spring Boot 3.5.6
1. 자동 설정 기능으로 개발 생산성을 향상시킬 수 있습니다.
2. 스타터 패키지를 통해 의존성 관리가 용이합니다.
3. 내장 서버를 제공하여 별도의 WAS 설정 없이 바로 실행 가능합니다.
4. 최신 버전으로 보안 패치와 성능 개선이 반영되어 있습니다.
2️⃣ PostgreSQL
1. 무료로 제공되는 오픈소스 RDBMS입니다.
2. 안정성과 확장성이 뛰어나며 대용량 데이터 처리에 적합합니다.
3. JSON 타입 지원 등 다양한 데이터 타입을 제공합니다.
4. ACID 특성을 완벽하게 지원하여 데이터 무결성을 보장합니다.
3️⃣ JWT & Spring Security
1. Stateless한 인증 방식으로 서버 확장성이 좋습니다.
2. Spring Security와의 완벽한 통합으로 보안 구현이 용이합니다.
3. 토큰 기반 인증으로 세션 관리 부담이 없습니다.
4. 역할 기반 접근 제어(RBAC)를 쉽게 구현할 수 있습니다.
4️⃣ Docker
1. 개발 환경과 운영 환경의 일관성을 유지할 수 있습니다.
2. 격리된 환경에서 테스트가 가능하여 시스템 영향을 최소화합니다.
3. 이미지 기반으로 빠른 배포가 가능합니다.
4. 마이크로서비스 아키텍처로의 확장이 용이합니다.
5️⃣ GitHub Actions
1. GitHub와 완벽하게 통합되어 별도의 CI/CD 도구 설정이 불필요합니다.
2. YAML 파일로 간단하게 워크플로우를 정의할 수 있습니다.
3. 자동화된 테스트와 배포로 개발 효율성을 높일 수 있습니다.
4. 무료 티어에서도 충분한 빌드 시간을 제공합니다.
6️⃣ RestClient
1. RestTemplate의 후속 버전으로 더 나은 성능과 유지보수성을 제공합니다.
2. 동기 방식으로 구현이 간단하고 학습 곡선이 낮습니다.
3. 예외 처리가 명확하고 직관적입니다.
4. 현재 프로젝트 규모에서는 비동기보다 동기 방식이 더 적합합니다.
7️⃣ Google Gemini API
1. 메뉴 설명 자동 생성으로 사장님의 업무 부담을 줄일 수 있습니다.
2. 자연스러운 한국어 생성이 가능합니다.
3. API 호출이 간단하여 빠르게 통합할 수 있습니다.
4. 무료 티어로 프로젝트 테스트가 가능합니다.
8️⃣ JUnit5 & Mockito
1. 단위 테스트 작성으로 코드 품질을 향상시킬 수 있습니다.
2. Mock 객체를 통해 의존성을 격리하여 테스트할 수 있습니다.
3. 테스트 자동화로 리팩토링 시 안전성을 확보할 수 있습니다.
4. Spring Boot와의 통합이 우수합니다.
곧감 아키텍처는 다음과 같이 구성됩니다:
- GitHub Actions를 통한 자동 빌드 및 테스트
- Docker 컨테이너화로 일관된 실행 환경 제공
- AWS EC2에 자동 배포
- PostgreSQL RDS로 데이터 관리
- EC2 인바운드 규칙을 통한 접근 제어
상세 테이블 구조
- 유저 (Users)
- 주소 (Address)
- 지역 (Region)
- 찜 (Favorite)
- 식당 (Restaurant)
- 식당 분류 카테고리 (Category)
- 메뉴 (Menu)
- 음식 (Food)
- 추가 옵션 (FoodOption)
- 리뷰 (Review)
- 장바구니 (Cart)
- 장바구니 별 음식 정보 (CartFood)
- 장바구니 내 음식 별 옵션 정보 (CartFoodOption)
- 주문 내역 (Order)
- 주문 내역 별 음식 정보 (OrderFood)
- 결제 (Payment)
- AI 로그 (AI)
✅ 인증/인가: JWT 기반 로그인/회원가입
✅ 주소 관리: 법정동 주소 데이터 연동, 사용자 주소 관리
✅ 식당/메뉴: 조회, 검색, 페이징, 사장님 전용 관리
✅ 장바구니: 담기/수정/삭제 및 합계 계산
✅ 주문/결제: 주문 생성/상태 관리, 결제 모듈 연동
✅ 리뷰/팔로우: 가게 리뷰 작성/조회, 팔로우 기반 피드
✅ AI: Google Gemini API를 활용한 메뉴 설명 생성
👨👩👧 유저: 로그인 | 회원가입 | JWT 인증 | 권한 관리
🏪 식당: 검색 | 조회 | 카테고리별 필터링 | 페이징 | 사장님 전용 관리
🍱 메뉴: 음식 조회 | 옵션 선택 | AI 설명 생성
🛒 장바구니: 담기 | 수정 | 삭제
📦 주문: 주문 생성 | 상태 관리 | 주문 내역 조회
💳 결제: 결제 처리 | 결제 내역 관리
⭐ 리뷰: 리뷰 작성 | 조회 | 평점 관리
❤️ 팔로우: 식당 팔로우 | 피드 조회
📍 주소: 주소 등록 | 관리 | 법정동 주소 연동
1️⃣ JWT 인증/인가
- Spring Security와 JWT를 활용한 Stateless 인증
- Customer와 Owner 역할 구분
- 토큰 기반 인증으로 확장성 확보
- @PreAuthorize를 통한 세밀한 권한 제어
2️⃣ 식당 관리
- 카테고리별 식당 조회
- 검색 및 페이징 기능
- 사장님 전용 식당 등록/수정/삭제
- 식당 상태 관리 (오픈/마감)
3️⃣ 장바구니 & 주문
- 장바구니에 음식 및 옵션 추가
- 장바구니 수정/삭제 기능
- 주문 생성 및 상태 관리
- 주문 내역 조회 및 상세 정보 확인
4️⃣ AI 메뉴 설명
- Google Gemini API 연동
- 메뉴명 기반 자동 설명 생성
- 사장님의 업무 효율화
- AI 호출 로그 저장
5️⃣ 리뷰 시스템
- 배달 완료된 주문에 대한 리뷰 작성
- 리뷰 수정/삭제 (Soft Delete)
- 주문별 리뷰 관리
- 리뷰 조회 기능
6️⃣ 주소 관리
- 법정동 주소 데이터 CSV 로딩
- 시도/시군구/법정동 계층 구조
- 사용자별 주소 등록/관리
- 기본 배송지 설정
7️⃣ 팔로우
- 식당 팔로우/언팔로우
- 팔로우한 식당 목록 조회
- 팔로우 상태 확인
GitHub Actions
```yaml
- 코드 체크아웃
- Gradle 빌드 및 테스트
- Docker 이미지 빌드
- EC2로 이미지 전송
- 컨테이너 실행
```
✅ 테스트 통과 후 자동 배포
✅ Docker를 통한 일관된 실행 환경
✅ EC2에서 안정적인 서비스 제공
Docker
```dockerfile
FROM openjdk:17-jdk-slim
COPY build/libs/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
```
✅ 개발/운영 환경 동일화
✅ 빠른 배포와 롤백
1️⃣ PostgreSQL 설치 오류 해결
문제
- PostgreSQL을 로컬에 설치하는 과정에서 에러 코드 1 발생
- 설치 경로에 한글이 포함되어 설치 실패
원인
- 한글 경로로 인한 설치 오류
해결 방안
- DBeaver를 사용하여 원격 RDS에 직접 접근
- 로컬 설치 없이 개발 환경 구성
결과
- ✅ 팀원 모두 동일한 DB 환경에서 작업 가능
- ✅ 초기 환경 구축 시간 단축
- ✅ 경로 오류 근본적 해결
2️⃣ Spring Security 권한 거부 응답 개선
문제
@PreAuthorize로 권한 제어 시 403 에러만 반환- Swagger에서 에러 상세 정보 확인 불가
원인
- 기본
AccessDeniedHandler가 HTML 기반 에러 페이지 반환 - API 클라이언트에서는 단순 403만 표시됨
해결 방안
- 커스텀 예외 처리 핸들러 구현
- JSON 형태의 상세 에러 응답 반환
결과
{
"status": 403,
"error": "Forbidden",
"message": "접근 권한이 없습니다.",
"path": "/api/v1/user"
}- ✅ 명확한 에러 메시지 제공
- ✅ 디버깅 효율 향상
- ✅ API 응답 일관성 확보
3️⃣ 주문 생성 시 다중 오류 해결
문제
- POST
/api/orders호출 시 순차적 오류 발생HttpMediaTypeNotAcceptableExceptionHttpMessageNotReadableException- SQL NOT NULL 제약 조건 위반
원인
- DTO에 getter 없어 JSON 변환 불가
@RequestBody String타입 불일치- 클래스 레벨
@Transactional(readOnly = true)로 INSERT 무시 - 엔티티와 DB 스키마 불일치
해결 방안
- DTO를
record로 변경하여 자동 getter 생성 - 요청 DTO 타입을 적절한 객체로 변경
- 쓰기 메서드에
@Transactional오버라이드 - DB 스키마와 엔티티 동기화
결과
- ✅ JSON 직렬화/역직렬화 정상 작동
- ✅ 트랜잭션 정상 커밋
- ✅ 불필요한 컬럼 제거
- ✅ 안정적인 주문 생성 기능
4️⃣ 회원가입 시 인증 정보 오류
문제
- 회원가입 시
UserDetailsImpl캐스팅 오류 발생 - SecurityContextHolder가 인증정보를 찾지 못함
원인
- 회원가입은 인증 없이 접근 가능한 API
- principal이 "anonymous" 문자열이라 캐스팅 불가
해결 방안
if(principal instanceof UserDetailsImpl){
return((UserDetailsImpl)principal).
getUsername();
}
return"SYSTEM";결과
- ✅ 미인증 사용자 작업 시 "SYSTEM" 기록
- ✅ 인증된 사용자는 닉네임 기록
- ✅ created_by, updated_by 추적 가능
5️⃣ Soft Delete 데이터 조회 문제
문제
- Cart 조회 시 삭제된 CartFood와 CartFoodOption까지 조회됨
- Soft Delete한 데이터가 함께 반환됨
원인
- 연관 엔티티에 조회 조건이 적용되지 않음
is_deleted = true인 데이터까지 전부 조회
해결 방안
@OneToMany(mappedBy = "cart")
@SQLRestriction("is_deleted = false")
private List<CartFood> cartFoods;결과
- ✅ 삭제되지 않은 데이터만 조회
- ✅ 데이터 정합성 유지
- ✅ 불필요한 필터링 로직 제거
6️⃣ 장바구니 음식 추가 시 ID 미생성 오류
문제
- 새 장바구니 생성 후 음식 추가 시 오류 발생
- 외래키 참조 실패
원인
Cart.create()로 생성한 엔티티를 저장하지 않고 사용- ID가 생성되지 않은 상태에서 참조 시도
해결 방안
Cart newCart = Cart.create(user, restaurant);
cartRepository.
save(newCart); // 저장 후 사용결과
- ✅ 안전한 데이터 저장 및 조회
- ✅ 외래키 참조 정상 작동
- ✅ 장바구니 기능 안정화
7️⃣ 식당 정보 접근 권한 문제
문제
- 식당 등록/수정/삭제 시 본인 확인 없이 접근 가능
- 다른 사장님의 식당 정보 수정 가능
원인
- 본인 ID 체크 로직 부재
- 권한만 확인하고 소유권은 미확인
해결 방안
if(!restaurant.getOwnerId().
equals(user.getId())){
throw new
UnauthorizedException("본인의 식당만 수정 가능합니다");
}결과
- ✅ 본인 식당만 관리 가능
- ✅ 권한과 소유권 이중 검증
- ✅ 데이터 보안 강화
8️⃣ 리뷰 재작성 시 Unique 제약 조건 위반
문제
- 리뷰 작성 → 삭제 → 재작성 시 오류 발생
- Soft Delete로 인해 실제 데이터는 남아있어 Unique 제약 위반
원인
- 주문과 리뷰가 1:1 관계
- 외래키에 Unique 제약 조건 존재
해결 방안
- 주문과 리뷰 관계를 1:N으로 변경
- 리뷰 생성 시 활성 상태 확인 로직 추가
if(reviewRepository.existsByOrderIdAndStatusTrue(orderId))







