Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
134 commits
Select commit Hold shift + click to select a range
8f91639
Add CLAUDE.md for project documentation
letter333 Feb 1, 2026
f0a5513
Add member signup design documents
letter333 Feb 2, 2026
68815c0
Merge branch 'Loopers-dev-lab:main' into main
letter333 Feb 2, 2026
d7cd9ba
Merge branch 'Loopers-dev-lab:main' into feature/member-signup
letter333 Feb 2, 2026
050dc5b
feat: add Member domain entity with field validation
letter333 Feb 2, 2026
1270fe7
Merge remote-tracking branch 'origin/feature/member-signup' into feat…
letter333 Feb 2, 2026
74ff057
feat: add Member infrastructure layer with repository and persistence…
letter333 Feb 2, 2026
65fffca
feat: 회원가입 비즈니스 로직 구현 (MemberService, PasswordEncoderConfig)
letter333 Feb 2, 2026
fd22458
feat: 회원가입 Application 레이어 구현 (MemberFacade, MemberInfo)
letter333 Feb 2, 2026
cf164da
feat: 회원가입 API 엔드포인트 구현 (Controller, DTO, ApiSpec, E2E 테스트)
letter333 Feb 2, 2026
4b80ed3
fix: 회원가입 API birthday null-safety 보완 및 .http 파일 추가
letter333 Feb 2, 2026
3ba45ed
docs: 내 정보 조회 설계 문서 추가 및 회원가입 시퀀스 다이어그램 보완
letter333 Feb 3, 2026
d2dcf7a
feat: 회원 조회를 위한 findByLoginId Repository 메서드 추가
letter333 Feb 3, 2026
5284c21
feat: 회원 인증 로직 구현 (authenticate, ErrorType.UNAUTHORIZED)
letter333 Feb 3, 2026
c429856
feat: 내 정보 조회 API 구현 및 통합 테스트 생성자 주입 통일
letter333 Feb 3, 2026
98e979e
test: Member 도메인 엔티티 필드 검증 경계값 테스트 추가
letter333 Feb 3, 2026
56fe6cd
Merge pull request #1 from letter333/feature/member-profile-lookup
letter333 Feb 3, 2026
c81b9d3
feat: Member 도메인 비밀번호 변경 로직 구현 및 단위 테스트 추가
letter333 Feb 4, 2026
5adb112
feat: 비밀번호 변경 서비스 로직 및 Repository 업데이트 구현
letter333 Feb 4, 2026
65b637e
feat: 비밀번호 변경 API 엔드포인트 및 E2E 테스트 구현
letter333 Feb 4, 2026
7b50186
docs: CLAUDE.md 프로젝트 규칙 보완
letter333 Feb 4, 2026
b6e6b42
Merge pull request #2 from letter333/feature/member-password-update
letter333 Feb 4, 2026
8e8d396
refactor: 비밀번호 변경 검증 책임을 서비스 레이어로 분리
letter333 Feb 4, 2026
04a22cf
Merge pull request #3 from letter333/feature/member-password-update
letter333 Feb 4, 2026
d5d4041
chore: .gitignore에 docs/study/ 학습 폴더 제외 추가
letter333 Feb 4, 2026
fa330aa
Merge branch 'feature/member-signup' of https://github.com/letter333/…
letter333 Feb 4, 2026
ff6be56
Merge pull request #4 from letter333/feature/member-signup
letter333 Feb 5, 2026
e622564
feat: 내 정보 조회 시 이름 마지막 글자 마스킹 처리
letter333 Feb 5, 2026
8879f6f
Merge pull request #5 from letter333/feature/member-profile-lookup
letter333 Feb 5, 2026
7da2806
feat: 로그인 ID 영문/숫자만 허용하는 검증 규칙 추가
letter333 Feb 5, 2026
767b39d
Merge pull request #6 from letter333/feature/member-profile-lookup
letter333 Feb 5, 2026
d2aa7cf
refactor: DTO에 Bean Validation 적용 및 도메인 검증 책임 분리
letter333 Feb 5, 2026
234ef00
merge: main 브랜치 병합 및 비밀번호 변경 Bean Validation 적용
letter333 Feb 5, 2026
7d34ed0
Merge pull request #7 from letter333/feature/member-profile-lookup
letter333 Feb 5, 2026
3310a3d
fix : 예제 테스트 코드 오류 해결을 위한 testcontainers 버전 업
madirony Feb 3, 2026
2a442e0
Merge pull request #48 from madirony/pr-only-commit
madirony Feb 8, 2026
f0e1d3e
docs: 설계 문서 작성 (요구사항, 시퀀스, 클래스, ERD)
letter333 Feb 12, 2026
a8440f3
docs: ERD 테이블 설계 및 관계 설정 개선
letter333 Feb 12, 2026
6e228ab
Merge branch 'Loopers-dev-lab:main' into main
letter333 Feb 12, 2026
673b513
docs: members 테이블에 role 컬럼 추가
letter333 Feb 13, 2026
bb3c626
docs: 요구사항 문서 구조화 및 배송지/카테고리 기능 추가
letter333 Feb 14, 2026
892e2e4
Merge pull request #8 from letter333/WEEK2
letter333 Feb 16, 2026
071e344
docs: active 컬럼 제거 및 설계 문서 일관성 개선
letter333 Feb 18, 2026
538c421
docs: active 컬럼 제거 및 설계 문서 일관성 개선
letter333 Feb 18, 2026
c03caa3
Merge branch 'WEEK3' of https://github.com/letter333/loop-pack-be-l2-…
letter333 Feb 18, 2026
ec85cb3
docs: 설계 문서 교차 검토 및 도메인 설계 개선
letter333 Feb 19, 2026
098b85d
docs: 카테고리 삭제 정책 및 배송지 개수 제한 규칙 추가
letter333 Feb 20, 2026
b3e15b7
feat: ErrorType FORBIDDEN 추가 및 AdminValidator 구현
letter333 Feb 20, 2026
243e30a
feat: Brand 도메인 구현 (TDD, 레이어드 아키텍처)
letter333 Feb 20, 2026
941ed2c
refactor: Brand Facade에 Command 패턴 적용
letter333 Feb 20, 2026
c382ca4
feat: Category 도메인 구현 (TDD, 레이어드 아키텍처)
letter333 Feb 22, 2026
18b1766
feat: Product 도메인 기본 생성 기능 구현 (TDD)
letter333 Feb 22, 2026
dd3769f
feat: Product 할인 기능 구현 (TDD)
letter333 Feb 22, 2026
779b09b
feat: Product update/delete/isAvailable 기능 구현 (TDD)
letter333 Feb 22, 2026
258336a
feat: Product Infrastructure Layer 구현
letter333 Feb 22, 2026
49f5c5b
feat: ProductService 구현 (TDD)
letter333 Feb 22, 2026
146b9dd
feat: ProductOption 도메인 및 Infrastructure Layer 구현 (TDD)
letter333 Feb 22, 2026
7c265e2
feat: ProductOptionService 구현 (TDD)
letter333 Feb 22, 2026
e756945
feat: ProductImage 도메인 및 Infrastructure Layer 구현 (TDD)
letter333 Feb 22, 2026
388c5fa
feat: Product Application/Interfaces Layer 구현 (TDD)
letter333 Feb 22, 2026
b3dcc28
refactor: QueryDSL → Native Query + FULLTEXT Index 변경
letter333 Feb 22, 2026
23b391e
feat: 상품 목록 조회 API 구현 (TDD)
letter333 Feb 22, 2026
a992dc4
refactor: getProductInfo → getProduct 이름 변경 및 getProductInfos 제거
letter333 Feb 22, 2026
868c96a
refactor: Product 애그리거트 재설계 및 DTO 리팩토링
letter333 Feb 23, 2026
53e457a
docs: Product 애그리거트 재설계 내용 설계 문서 반영
letter333 Feb 23, 2026
7b971eb
refactor: 트랜잭션 전파 전략 적용
letter333 Feb 23, 2026
cb53ad7
feat: 브랜드/카테고리 삭제 시 연관 상품 Soft Delete 구현
letter333 Feb 23, 2026
12bbb95
feat: Like 도메인 구현 - 상품/브랜드 좋아요 기능
letter333 Feb 23, 2026
ba3be33
feat: 배송지 삭제 시 자동 기본 배송지 설정 기능 추가
letter333 Feb 23, 2026
68a5112
refactor: 배송지 삭제 방식을 Soft Delete에서 Hard Delete로 변경
letter333 Feb 23, 2026
d4eba17
feat: 배송지(Address) 도메인 기능 구현
letter333 Feb 23, 2026
46f8454
fix: Like 토글 동시성 처리 개선 - DataIntegrityViolationException 핸들링
letter333 Feb 23, 2026
79cfa44
feat: Order 도메인 Enum 추가 (OrderStatus, OrderProductStatus, OrderPeriod)
letter333 Feb 25, 2026
072fb79
feat: Order, OrderProduct 도메인 객체 구현 (TDD)
letter333 Feb 25, 2026
53c9b50
feat: OrderRepository 인터페이스 및 OrderService 구현 (TDD)
letter333 Feb 25, 2026
900ae99
feat: Order Infrastructure Layer 구현 (Entity, Repository)
letter333 Feb 25, 2026
bad8fd6
feat: Order Application Layer 구현 (TDD)
letter333 Feb 25, 2026
1bda828
feat: Order Interfaces Layer 구현 (Controller, DTO, E2E 테스트)
letter333 Feb 25, 2026
8963446
fix: Order 주문 취소 시 재고 복구 오류 수정
letter333 Feb 25, 2026
93ebfb6
refactor: Order 금액 필드 타입을 Long으로 변경
letter333 Feb 25, 2026
9420495
refactor: Order 배송지 컬럼명에 recipient_ 접두사 추가
letter333 Feb 25, 2026
1eb043b
refactor: Order shipping_memo 컬럼 길이 확장 (255 → 500)
letter333 Feb 25, 2026
5e6a2cf
refactor: OrderProduct option_name을 option_value로 변경
letter333 Feb 25, 2026
c0a1236
docs: ERD에 order_products.thumbnail_url 컬럼 추가
letter333 Feb 25, 2026
7ad1f6b
refactor: 설계문서와 구현 정합성 수정
letter333 Feb 25, 2026
b839a82
feat: Admin 상품 목록 조회 API 구현 (ADM-050, ADM-051)
letter333 Feb 25, 2026
fb3429d
feat: Admin 주문 상태 변경 API 구현 (OAD-012, OAD-013)
letter333 Feb 25, 2026
17aaa5d
refactor: 상품 도메인 검증 로직 강화 및 테스트 보완
letter333 Feb 26, 2026
2b7f9dd
refactor: Product 도메인 검증 로직 보완 및 테스트 강화
letter333 Feb 26, 2026
d6db780
feat: Product SOLDOUT 자동 전환 로직 및 LIKES_DESC 정렬 테스트 추가
letter333 Feb 26, 2026
de2fee5
test: LIKES_DESC 정렬 및 주문-재고 SOLDOUT 연계 테스트 보강
letter333 Feb 26, 2026
1e3d2a2
chore: HTTP 파일 디렉토리 구조 통일
letter333 Feb 26, 2026
0ce00c3
refactor: 트랜잭션 경계 최적화, N+1 쿼리 개선 및 Admin API E2E 테스트 추가
letter333 Feb 26, 2026
61bdeed
Merge pull request #9 from letter333/WEEK3
letter333 Mar 3, 2026
42b7071
fix: Order E2E 테스트 JPA 1차 캐시 문제 수정
letter333 Mar 3, 2026
83c6388
Merge pull request #10 from letter333/fix/order-e2e-test-cache
letter333 Mar 3, 2026
2837576
docs: 쿠폰 기능 ERD 문서 추가
letter333 Mar 3, 2026
4655153
feat: 쿠폰 도메인 레이어 구현
letter333 Mar 3, 2026
6c3e1c5
feat: 쿠폰 인프라스트럭처 레이어 구현
letter333 Mar 3, 2026
6ddd510
feat: 쿠폰 애플리케이션 레이어 구현
letter333 Mar 3, 2026
40e14eb
feat: 쿠폰 Admin API 구현
letter333 Mar 3, 2026
4ca7eac
feat: 쿠폰 User API 구현
letter333 Mar 3, 2026
f82db20
feat: 주문 시 쿠폰 적용 및 취소 시 복구 기능 추가
letter333 Mar 3, 2026
5d9a3d7
fix: OrderV1ApiE2ETest Set 순서 비보장 문제 수정
letter333 Mar 3, 2026
f9341ab
docs: 쿠폰 기능 문서를 기존 설계 문서에 병합
letter333 Mar 3, 2026
e81467f
feat: 쿠폰 발급 내역 조회 API 추가 및 CouponType 네이밍 변경
letter333 Mar 3, 2026
4205f7a
Merge pull request #11 from letter333/feat/coupon
letter333 Mar 3, 2026
050f2bb
feat: 배송비 자동 계산 기능 구현
letter333 Mar 3, 2026
22ad273
Merge pull request #12 from letter333/feat/shipping-fee
letter333 Mar 3, 2026
f3111f8
fix: Order 도메인의 중복 applyCouponDiscount 메서드 제거
letter333 Mar 3, 2026
2b5a4fa
refactor: MemberCoupon.setCoupon() 제거 및 생성자 주입으로 캡슐화 개선
letter333 Mar 3, 2026
c109977
refactor: 쿠폰 도메인 리팩토링 - Facade 의존 제거, 페이지네이션, 데드 코드 정리
letter333 Mar 4, 2026
8ed417b
refactor: @Component → @Service/@Repository 어노테이션 일관성 통일
letter333 Mar 4, 2026
214f00d
fix: BrandResponse에 누락된 likeCount 필드 추가
letter333 Mar 4, 2026
c6654bc
refactor: 미사용 findAllActiveByParentId() 데드 코드 제거
letter333 Mar 4, 2026
acb7c9c
fix: Member 생성자 간 유효성 검증 불일치 수정
letter333 Mar 4, 2026
4405aa0
refactor: Member 복원 생성자에서 불필요한 validateBirthday() 호출 제거
letter333 Mar 4, 2026
730fff4
refactor: 쿠폰 도메인 어노테이션 통일, 쿼리 최적화 및 로직 추출
letter333 Mar 4, 2026
d0361fc
refactor: 쿠폰 도메인 데드 코드 제거 및 useCoupon 이중 조회 최적화
letter333 Mar 4, 2026
092f1ec
Merge pull request #13 from letter333/WEEK4-simplify
letter333 Mar 4, 2026
b3631ed
feat: 재고 차감 동시성 문제 해결 — 비관적 락(Pessimistic Lock) 적용
letter333 Mar 4, 2026
f57c87e
feat: 쿠폰 발급 동시성 문제 해결 — 비관적 락(Pessimistic Lock) 적용
letter333 Mar 5, 2026
2c72863
feat: 좋아요 수(likeCount) 동시성 문제 해결 — Product 비관적 락, Brand 낙관적 락 적용
letter333 Mar 5, 2026
0925970
feat: 쿠폰 사용(useCoupon) 동시성 문제 해결 — 비관적 락(Pessimistic Lock) 적용
letter333 Mar 5, 2026
0c58fe5
feat: 주문 취소(cancelOrder) 동시성 문제 해결 — 비관적 락(Pessimistic Lock) 적용
letter333 Mar 5, 2026
5df7d4c
fix: 다중 Product 락 획득 시 데드락 방지 — productId 오름차순 정렬 적용
letter333 Mar 5, 2026
67b4c2c
refactor: OrderService.cancelOrder() 제거 — 취소 로직을 OrderFacade로 일원화
letter333 Mar 5, 2026
be36c5f
chore: 과제 체크리스트(check.md) 파일 삭제
letter333 Mar 5, 2026
8d346db
Merge pull request #15 from letter333/WEEK4-concurrency
letter333 Mar 5, 2026
2bfecd4
refactor: 좋아요 retry 최적화, 쿠폰 TOCTOU 제거, 존재 확인 경량화
letter333 Mar 6, 2026
69d7f99
refactor: dead code 제거 및 테스트 중복 모킹 헬퍼 추출
letter333 Mar 6, 2026
1ec55b9
refactor: OrderFacade 주문 취소 시 재고 복구+쿠폰 취소 중복 로직 메서드 추출
letter333 Mar 6, 2026
7b6dd5e
refactor: 좋아요 카운트를 원자적 네이티브 UPDATE로 전환 및 Facade 동시성 테스트 추가
letter333 Mar 6, 2026
cdc54c9
refactor: MemberCoupon 비관적 락 → 낙관적 락 전환 및 Facade 재시도 추가
letter333 Mar 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ out/

### Kotlin ###
.kotlin

### Study ###
docs/study/
200 changes: 200 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
# CLAUDE.md

## 프로젝트 개요

Spring Boot 기반 커머스 멀티모듈 템플릿 프로젝트 (`loopers-java-spring-template`).
REST API, 배치 처리, 이벤트 스트리밍을 위한 마이크로서비스 아키텍처 패턴을 제공한다.

## 기술 스택 및 버전

| 구분 | 기술 | 버전 |
|------|------|------|
| Language | Java | 21 |
| Language | Kotlin | 2.0.20 |
| Framework | Spring Boot | 3.4.4 |
| Framework | Spring Cloud | 2024.0.1 |
| Dependency Mgmt | spring-dependency-management | 1.1.7 |
| Database | MySQL | 8.0 |
| ORM | Spring Data JPA + QueryDSL | (Spring 관리) |
| Cache | Redis (Master-Replica) | 7.0 |
| Messaging | Apache Kafka (KRaft) | 3.5.1 |
| API Docs | SpringDoc OpenAPI | 2.7.0 |
| Monitoring | Micrometer + Prometheus | (Spring 관리) |
| Tracing | Micrometer Brave | (Spring 관리) |
| Logging | Logback + Slack Appender | 1.6.1 |
| Testing | JUnit 5, Mockito 5.14.0, SpringMockK 4.0.2, Instancio 5.0.2 |
| Testing Infra | TestContainers (MySQL, Redis, Kafka) | (Spring 관리) |
| Code Coverage | JaCoCo | (Gradle 관리) |
| Build Tool | Gradle (Kotlin DSL) | Wrapper 포함 |

## 모듈 구조

```
root
├── apps/ # 실행 가능한 Spring Boot 애플리케이션
│ ├── commerce-api # REST API 서버 (Tomcat)
│ ├── commerce-batch # Spring Batch 배치 처리
│ └── commerce-streamer # Kafka Consumer 스트리밍 서비스
├── modules/ # 재사용 가능한 인프라 모듈 (java-library)
│ ├── jpa # JPA + MySQL + QueryDSL + HikariCP
│ ├── redis # Redis Master-Replica (Lettuce)
│ └── kafka # Kafka Producer/Consumer 설정
├── supports/ # 횡단 관심사 모듈
│ ├── jackson # Jackson ObjectMapper 커스터마이징
│ ├── logging # 구조화 로깅 + Slack 연동
│ └── monitoring # Prometheus 메트릭 + Health Probe
└── docker/
├── infra-compose.yml # MySQL, Redis, Kafka, Kafka UI
└── monitoring-compose.yml # Prometheus, Grafana
```

## 아키텍처 레이어 (commerce-api 기준)

```
interfaces/api/ → REST Controller, DTO, ApiSpec
application/ → Facade (유즈케이스 오케스트레이션), Info DTO
domain/ → Entity, Repository 인터페이스, Service (비즈니스 로직)
infrastructure/ → Repository 구현체
support/error/ → CoreException, ErrorType
```

## 빌드 및 실행

```bash
# 인프라 구동
docker compose -f docker/infra-compose.yml up -d

# 모니터링 스택 (Grafana: localhost:3000, admin/admin)
docker compose -f docker/monitoring-compose.yml up -d

# 빌드
./gradlew clean build

# 실행
./gradlew :apps:commerce-api:bootRun
./gradlew :apps:commerce-batch:bootRun --args='--job.name=demoJob'
./gradlew :apps:commerce-streamer:bootRun

# 테스트
./gradlew test

# 코드 커버리지
./gradlew test jacocoTestReport
```

## 테스트 설정

- JUnit 5 기반, 테스트 프로파일: `test`, 타임존: `Asia/Seoul`
- TestContainers로 MySQL/Redis/Kafka 통합 테스트
- 모듈별 `testFixtures`로 테스트 유틸리티 공유 (`DatabaseCleanUp`, `RedisCleanUp` 등)
- 테스트 병렬 실행 없음 (`maxParallelForks = 1`)

## 프로파일

`local`, `test`, `dev`, `qa`, `prd` — 환경별 설정은 각 모듈의 yml 파일에서 프로파일 그룹으로 관리.
운영 환경은 환경변수로 주입: `MYSQL_HOST`, `REDIS_MASTER_HOST`, `BOOTSTRAP_SERVERS` 등.

## 주요 패턴

- **BaseEntity**: ID 자동생성, `createdAt`/`updatedAt` 감사, `deletedAt` 소프트 삭제
- **ApiResponse**: 통일된 응답 래퍼 (`meta.result`, `meta.errorCode`, `data`)
- **CoreException + ErrorType**: 타입 기반 에러 처리 (400, 404, 409, 500)
- **별도 관리 포트**: 메트릭/헬스체크는 8081 포트로 분리
- **Kafka 배치 소비**: 3000건 배치, 수동 커밋 (Manual ACK)
- **Redis 읽기 분산**: Master 쓰기, Replica 읽기 분리

## API 응답 규칙
- 모든 응답은 `ApiResponse<T>`로 래핑
- 성공: `ApiResponse.success(data)` 반환
- 실패: `CoreException(ErrorType)` throw → GlobalExceptionHandler에서 처리
- 생성 API: `@ResponseStatus(HttpStatus.CREATED)`

## 의존성 방향 (외부 → 내부)
```
interfaces → application → domain ← infrastructure
```
- domain 계층은 다른 계층에 의존하지 않음
- infrastructure는 domain의 Repository 인터페이스를 구현

## 문서 작성
### 다이어그램 작성
- ERD, 시퀀스 다이어그램, 클래스 다이어그램 등 작성 시 mermaid를 이용한 마크다운으로 작성.

## 개발 규칙
### 진행 Workflow - 증강 코딩
- **대원칙** : 방향성 및 주요 의사 결정은 개발자에게 제안만 할 수 있으며, 최종 승인된 사항을 기반으로 작업을 수행.
- **중간 결과 보고** : AI 가 반복적인 동작을 하거나, 요청하지 않은 기능을 구현, 테스트 삭제를 임의로 진행할 경우 개발자가 개입.
- **설계 주도권 유지** : AI 가 임의판단을 하지 않고, 방향성에 대한 제안 등을 진행할 수 있으나 개발자의 승인을 받은 후 수행.
- 구현은 한 단계씩 순서대로 진행 및 단계가 끝날 때 마다 핵심 개념/키워드 설명.
- API는 RESTFul API로 구현
### 인증 요청
- 유저 정보가 필요한 모든 요청은 아래 헤더를 통해 요청
* X-Loopers-LoginId : 로그인 ID
* X-Loopers-LoginPw : 비밀번호
- Admin 기능은 아래 헤더를 통해 Admin 식별 후 제공
* X-Loopers-Ldap : loopers.admin

### 개발 Workflow - TDD (Red > Green > Refactor)
- 모든 테스트는 3A 원칙으로 작성할 것 (Arrange - Act - Assert)
#### 1. Red Phase : 실패하는 테스트 먼저 작성
- 요구사항을 만족하는 기능 테스트 케이스 작성
- 테스트 예시
#### 2. Green Phase : 테스트를 통과하는 코드 작성
- Red Phase 의 테스트가 모두 통과할 수 있는 코드 작성
- 오버엔지니어링 금지
#### 3. Refactor Phase : 불필요한 코드 제거 및 품질 개선
- 불필요한 private 함수 지양, 객체지향적 코드 작성
- unused import 제거
- 성능 최적화
- 모든 테스트 케이스가 통과해야 함

## 주의사항
### 1. Never Do
- 실제 동작하지 않는 코드, 불필요한 Mock 데이터를 이용한 구현을 하지 말 것
- null-safety 하지 않게 코드 작성하지 말 것 (Java 의 경우, Optional 을 활용할 것)
- println 코드 남기지 말 것
- 객체지향 5원칙을 어기지 말 것

### 2. Recommendation
- 실제 API 를 호출해 확인하는 E2E 테스트 코드 작성
- 재사용 가능한 객체 설계
- 성능 최적화에 대한 대안 및 제안
- 개발 완료된 API 의 경우, `.http/**.http` 에 분류해 작성
- Domain Entity와 Persistence Entity는 구분하여 구현
- 필요한 의존성은 적절히 관리하여 최소화
- 통합 테스트는 테스트 컨테이너를 이용해 진행
- 테스트 코드 작성 시 MIN, MAX, EDGE 케이스를 고려하여 작성
- Lombok 활용이 가능한 부분은 Lombok을 활용하여 코드를 간결하게 작성
- VO (Value Object) 활용이 가능한 부분은 VO를 활용하여 코드를 간결하게 작성하되 남발하지 말것

### 3. Priority
1. 실제 동작하는 해결책만 고려
2. null-safety, thread-safety 고려
3. 테스트 가능한 구조로 설계
4. 기존 코드 패턴 분석 후 일관성 유지

## 깃 커밋 컨벤션
- feat: 새로운 기능 추가
- fix: 버그 수정
- docs: 문서만 수정 (예: README, 주석은 아님)
- style: 코드 포맷팅 (공백, 세미콜론 등 기능 변화 없음)
- refactor: 기능 변화 없이 코드 개선
- test: 테스트 코드 추가/수정
- chore: 빌드/패키지 설정 등 기능과 직접 관련 없는 작업
- 커밋 메세지는 한국어로 작성할 것

## 도메인 & 객체 설계 전략
- 도메인 객체는 비즈니스 규칙을 캡슐화해야 합니다.
- 애플리케이션 서비스는 서로 다른 도메인을 조립해, 도메인 로직을 조정하여 기능을 제공해야 합니다.
- 규칙이 여러 서비스에 나타나면 도메인 객체에 속할 가능성이 높습니다.
- 각 기능에 대한 책임과 결합도에 대해 개발자의 의도를 확인하고 개발을 진행합니다.

## 아키텍처, 패키지 구성 전략
- 본 프로젝트는 레이어드 아키텍처를 따르며, DIP (의존성 역전 원칙) 을 준수합니다.
- API request, response DTO와 응용 레이어의 DTO는 분리해 작성하도록 합니다.
- 패키징 전략은 4개 레이어 패키지를 두고, 하위에 도메인 별로 패키징하는 형태로 작성합니다.
- 예시
> /interfaces/api (presentation 레이어 - API)
/application/.. (application 레이어 - 도메인 레이어를 조합해 사용 가능한 기능을 제공)
/domain/.. (domain 레이어 - 도메인 객체 및 엔티티, Repository 인터페이스가 위치)
/infrastructure/.. (infrastructure 레이어 - JPA, Redis 등을 활용해 Repository 구현체를 제공)
8 changes: 8 additions & 0 deletions apps/commerce-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@ dependencies {
implementation(project(":supports:logging"))
implementation(project(":supports:monitoring"))

// security
implementation("org.springframework.security:spring-security-crypto")

// retry
implementation("org.springframework.retry:spring-retry")
implementation("org.springframework:spring-aspects")

// web
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:${project.properties["springDocOpenApiVersion"]}")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.retry.annotation.EnableRetry;
import java.util.TimeZone;

@EnableRetry
@ConfigurationPropertiesScan
@SpringBootApplication
public class CommerceApiApplication {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.loopers.application.address;

public class AddressCommand {

public record Create(
String recipientName,
String phone,
String zipCode,
String address,
String addressDetail
) {
}

public record Update(
String recipientName,
String phone,
String zipCode,
String address,
String addressDetail
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.loopers.application.address;

import com.loopers.domain.address.Address;
import com.loopers.domain.address.AddressService;
import com.loopers.domain.member.Member;
import com.loopers.domain.member.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Component
@RequiredArgsConstructor
public class AddressFacade {

private final AddressService addressService;
private final MemberService memberService;

@Transactional(readOnly = true)
public List<AddressInfo> getAddresses(String loginId, String password) {
Member member = memberService.authenticate(loginId, password);
return addressService.getAddresses(member.getId())
.stream()
.map(AddressInfo::from)
.toList();
}

@Transactional
public AddressInfo register(String loginId, String password, AddressCommand.Create command) {
Member member = memberService.authenticate(loginId, password);
Address address = new Address(
member.getId(),
command.recipientName(),
command.phone(),
command.zipCode(),
command.address(),
command.addressDetail()
);
Address savedAddress = addressService.register(address);
return AddressInfo.from(savedAddress);
}

@Transactional
public AddressInfo update(String loginId, String password, Long addressId, AddressCommand.Update command) {
Member member = memberService.authenticate(loginId, password);
Address updatedAddress = addressService.update(
member.getId(),
addressId,
command.recipientName(),
command.phone(),
command.zipCode(),
command.address(),
command.addressDetail()
);
return AddressInfo.from(updatedAddress);
}

@Transactional
public void delete(String loginId, String password, Long addressId) {
Member member = memberService.authenticate(loginId, password);
addressService.delete(member.getId(), addressId);
}

@Transactional
public AddressInfo setDefault(String loginId, String password, Long addressId) {
Member member = memberService.authenticate(loginId, password);
Address updatedAddress = addressService.setDefault(member.getId(), addressId);
return AddressInfo.from(updatedAddress);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.loopers.application.address;

import com.loopers.domain.address.Address;

public record AddressInfo(
Long id,
String recipientName,
String phone,
String zipCode,
String address,
String addressDetail,
boolean isDefault
) {

public static AddressInfo from(Address address) {
return new AddressInfo(
address.getId(),
address.getRecipientName(),
address.getPhone(),
address.getZipCode(),
address.getAddress(),
address.getAddressDetail(),
address.isDefault()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.loopers.application.brand;

public class BrandCommand {

public record Create(
String name,
String description,
String logoImageUrl
) {}

public record Update(
String name,
String description,
String logoImageUrl
) {}
}
Loading