Skip to content

[volume-1] 회원가입/내 정보 조회/비밀번호 수정 기능 작성#7

Merged
discphy merged 18 commits intoLoopers-dev-lab:discphyfrom
discphy:volume-1
Feb 3, 2026
Merged

[volume-1] 회원가입/내 정보 조회/비밀번호 수정 기능 작성#7
discphy merged 18 commits intoLoopers-dev-lab:discphyfrom
discphy:volume-1

Conversation

@discphy
Copy link
Copy Markdown
Member

@discphy discphy commented Feb 3, 2026

📌 Summary

  • 배경: 회원 관리 기능이 없어 사용자 인증 및 정보 관리가 불가능했음
  • 목표: 회원가입, 내 정보 조회, 비밀번호 수정 API 구현
  • 결과: 비밀번호 암호화, 검증 규칙, 이름 마스킹을 포함한 회원 관리 시스템 완성

🧭 Context & Decision

문제 정의

  • 현재 동작/제약: 회원 관련 기능이 전혀 없음
  • 문제(또는 리스크): 사용자 식별 및 인증 불가
  • 성공 기준(완료 정의): 회원가입/조회/비밀번호 수정 API가 정상 동작

선택지와 결정

  • 고려한 대안:
    • A: Spring Security 전체 적용 (세션/JWT 기반 인증)
    • B: 헤더 기반 간단한 인증 (X-Loopers-LoginId, X-Loopers-LoginPw)
  • 최종 결정: B안 (헤더 기반 인증)
  • 트레이드오프: 보안성은 낮지만 구현 단순화, 요구사항에 명시된 방식 준수
  • 추후 개선 여지: JWT 토큰 기반 인증으로 전환 가능

🏗️ Design Overview

변경 범위

  • 영향 받는 모듈/도메인: commerce-api
  • 신규 추가: member 도메인 전체, PasswordEncoderConfig
  • 제거/대체: 없음

주요 컴포넌트 책임

  • MemberModel: 회원 엔티티 (loginId, password, name, birthDate, email)
  • MemberService: 회원가입, 인증, 비밀번호 변경 비즈니스 로직 및 검증
  • MemberFacade: 컨트롤러와 서비스 간 조율, 마스킹 처리
  • MemberV1Controller: REST API 엔드포인트 제공

🔁 Flow Diagram

Main Flow - 회원가입

sequenceDiagram
  autonumber
  participant Client
  participant Controller
  participant Facade
  participant Service
  participant Repository
  participant DB

  Client->>Controller: POST /api/v1/members
  Controller->>Facade: register(loginId, password, name, birthDate, email)
  Facade->>Service: register(...)
  Service->>Service: validateLoginId, validatePassword, validateName, validateEmail
  Service->>Repository: existsByLoginId(loginId)
  Repository->>DB: SELECT
  DB-->>Repository: false
  Service->>Service: passwordEncoder.encode(password)
  Service->>Repository: save(member)
  Repository->>DB: INSERT
  DB-->>Repository: member
  Repository-->>Service: member
  Service-->>Facade: MemberModel
  Facade-->>Controller: MemberInfo
  Controller-->>Client: ApiResponse<RegisterResponse>
Loading

Main Flow - 내 정보 조회

sequenceDiagram
  autonumber
  participant Client
  participant Controller
  participant Facade
  participant Service
  participant Repository
  participant DB

  Client->>Controller: GET /api/v1/members/me (headers: LoginId, LoginPw)
  Controller->>Facade: getMyInfo(loginId, password)
  Facade->>Service: authenticate(loginId, password)
  Service->>Repository: findByLoginId(loginId)
  Repository->>DB: SELECT
  DB-->>Repository: member
  Service->>Service: passwordEncoder.matches(password, encodedPassword)
  Service-->>Facade: MemberModel
  Facade->>Facade: maskLastCharacter(name)
  Facade-->>Controller: MemberInfo (masked)
  Controller-->>Client: ApiResponse<MemberResponse>
Loading

Main Flow - 비밀번호 수정

sequenceDiagram
  autonumber
  participant Client
  participant Controller
  participant Facade
  participant Service
  participant Repository
  participant DB

  Client->>Controller: PUT /api/v1/members/password (headers + body)
  Controller->>Facade: changePassword(loginId, currentPw, newPw)
  Facade->>Service: changePassword(...)
  Service->>Service: authenticate(loginId, currentPw)
  Service->>Service: check newPw != currentPw
  Service->>Service: validatePassword(newPw, birthDate)
  Service->>Service: passwordEncoder.encode(newPw)
  Service->>Repository: (dirty checking)
  Repository->>DB: UPDATE
  DB-->>Repository: ok
  Service-->>Facade: void
  Facade-->>Controller: void
  Controller-->>Client: ApiResponse.success()
Loading

hanyoung-kurly and others added 18 commits January 25, 2026 13:01
- Add ProductModel entity with name, description, price, stock fields
- Add ProductRepository interface for domain abstraction
- Add ProductService with CRUD business logic

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ProductJpaRepository extending Spring Data JPA
- Add ProductRepositoryImpl implementing domain repository
- Support soft delete with findAllByDeletedAtIsNull

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ProductInfo record for application DTO
- Add ProductFacade for coordinating service calls

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ProductV1Dto with Create/Update requests and responses
- Add ProductV1ApiSpec with OpenAPI annotations
- Add ProductV1Controller with CRUD endpoints:
  - POST /api/v1/products (create)
  - GET /api/v1/products/{id} (read single)
  - GET /api/v1/products (read list with pagination)
  - PUT /api/v1/products/{id} (update)
  - DELETE /api/v1/products/{id} (soft delete)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Member 도메인 모델 및 레포지토리 생성
- 회원가입 API (POST /api/v1/members) 구현
- 비밀번호 검증: 8~16자, 영문대소문자/숫자/특수문자, 생년월일 포함 불가
- 로그인ID/이름/이메일 형식 검증
- 중복 로그인ID 검증
- BCrypt 비밀번호 암호화

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 내 정보 조회 API (GET /api/v1/members/me) 구현
- X-Loopers-LoginId, X-Loopers-LoginPw 헤더를 통한 인증
- 이름 마지막 글자 마스킹 처리 (*)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 비밀번호 수정 API (PUT /api/v1/members/password) 구현
- 기존 비밀번호 검증 후 새 비밀번호로 변경
- 현재 비밀번호와 동일한 비밀번호 사용 불가
- 비밀번호 규칙 검증 (8~16자, 생년월일 포함 불가)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@discphy discphy self-assigned this Feb 3, 2026
Copilot AI review requested due to automatic review settings February 3, 2026 16:36
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 3, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • 🔍 Trigger a full review
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Important

Action Needed: IP Allowlist Update

If your organization protects your Git platform with IP whitelisting, please add the new CodeRabbit IP address to your allowlist:

  • 136.113.208.247/32 (new)
  • 34.170.211.100/32
  • 35.222.179.152/32

Failure to add the new IP will result in interrupted reviews.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@discphy discphy changed the base branch from discphy to main February 3, 2026 16:37
@discphy discphy changed the base branch from main to discphy February 3, 2026 16:37
@discphy discphy merged commit 3100260 into Loopers-dev-lab:discphy Feb 3, 2026
5 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

회원 관리 기능(회원가입/내 정보 조회/비밀번호 변경)을 추가하고, 비밀번호 암호화를 위한 PasswordEncoder 설정을 도입한다. 추가로 상품 CRUD API/도메인도 함께 포함되며, PR 템플릿/리뷰봇 설정 및 기존 GitHub Actions 워크플로우 제거가 동반된다.

Changes:

  • Member 도메인/애플리케이션/REST API 추가(회원가입, 내 정보 조회(이름 마스킹), 비밀번호 변경 + 유효성 검증/암호화)
  • Product 도메인/애플리케이션/REST API 추가(CRUD + 페이징 조회 + soft delete 기반 삭제)
  • PasswordEncoder 설정 추가, PR 템플릿/CodeRabbit 설정 추가 및 기존 PR Agent 워크플로우 제거

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
apps/commerce-api/src/main/java/com/loopers/support/PasswordEncoderConfig.java BCrypt 기반 PasswordEncoder 빈 등록
apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/ProductV1Dto.java 상품 요청/응답 DTO 및 Page 응답 매핑 추가
apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/ProductV1Controller.java 상품 CRUD + 목록 API 엔드포인트 추가
apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/ProductV1ApiSpec.java 상품 API Swagger 스펙 인터페이스 추가
apps/commerce-api/src/main/java/com/loopers/interfaces/api/member/MemberV1Dto.java 회원 요청/응답 DTO 추가
apps/commerce-api/src/main/java/com/loopers/interfaces/api/member/MemberV1Controller.java 회원가입/내정보/비밀번호변경 API 엔드포인트 추가
apps/commerce-api/src/main/java/com/loopers/interfaces/api/member/MemberV1ApiSpec.java 회원 API Swagger 스펙 인터페이스 추가
apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java 상품 리포지토리 어댑터 구현(soft delete, 페이징 조회)
apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java 상품 Spring Data JPA 리포지토리 추가
apps/commerce-api/src/main/java/com/loopers/infrastructure/member/MemberRepositoryImpl.java 회원 리포지토리 어댑터 구현 추가
apps/commerce-api/src/main/java/com/loopers/infrastructure/member/MemberJpaRepository.java 회원 Spring Data JPA 리포지토리 추가
apps/commerce-api/src/main/java/com/loopers/domain/product/ProductService.java 상품 생성/조회/수정/삭제 도메인 서비스 추가
apps/commerce-api/src/main/java/com/loopers/domain/product/ProductRepository.java 상품 도메인 리포지토리 인터페이스 추가
apps/commerce-api/src/main/java/com/loopers/domain/product/ProductModel.java 상품 엔티티 및 생성/수정 검증 로직 추가
apps/commerce-api/src/main/java/com/loopers/domain/member/MemberService.java 회원 등록/인증/비밀번호 변경 및 검증 로직 추가
apps/commerce-api/src/main/java/com/loopers/domain/member/MemberRepository.java 회원 도메인 리포지토리 인터페이스 추가
apps/commerce-api/src/main/java/com/loopers/domain/member/MemberModel.java 회원 엔티티 추가
apps/commerce-api/src/main/java/com/loopers/application/product/ProductInfo.java 상품 응답용 Info 레코드 추가
apps/commerce-api/src/main/java/com/loopers/application/product/ProductFacade.java 상품 유스케이스 파사드 추가
apps/commerce-api/src/main/java/com/loopers/application/member/MemberInfo.java 회원 Info 레코드 및 이름 마스킹 로직 추가
apps/commerce-api/src/main/java/com/loopers/application/member/MemberFacade.java 회원 유스케이스 파사드 추가
apps/commerce-api/build.gradle.kts spring-security-crypto 의존성 추가
.github/workflows/main.yml PR Agent GitHub Actions 워크플로우 제거
.github/pull_request_template.md PR 템플릿 개편(의사결정/설계/플로우 중심)
.coderabbit.yaml CodeRabbit 리뷰 설정 신규 추가
.codeguide/loopers-1-week.md 가이드 문서 제거
Comments suppressed due to low confidence (1)

.github/pull_request_template.md:68

  • PR 템플릿의 Mermaid 예시 코드 블록이 닫히지 않아(닫는 ``` 누락) 이후 내용이 전부 코드 블록으로 렌더링된다. Mermaid 블록 끝에 닫는 코드 펜스를 추가해야 한다.
### Main Flow
```mermaid
sequenceDiagram
  autonumber
  participant Client
  participant API
  participant Service
  participant DB
  Client->>API: request
  API->>Service: command/query
  Service->>DB: read/write
  DB-->>Service: result
  Service-->>API: response
  API-->>Client: response


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


@Override
public Optional<ProductModel> find(Long id) {
return productJpaRepository.findById(id);
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Product는 soft delete(BaseEntity.deletedAt)를 사용하고 있는데, find(id)가 단순 findById만 호출해서 삭제된 상품도 조회/수정/삭제 대상이 될 수 있다. 삭제된 상품을 NOT_FOUND로 처리하려면 repository 단에서 deletedAt IS NULL 조건을 포함한 조회(예: findByIdAndDeletedAtIsNull)를 사용하고, ProductService.getProduct()가 그 메서드를 사용하도록 정리하는 것이 안전하다.

Suggested change
return productJpaRepository.findById(id);
return productJpaRepository.findByIdAndDeletedAtIsNull(id);

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +29
@Override
public List<ProductModel> findAll() {
return productJpaRepository.findAll().stream()
.filter(product -> product.getDeletedAt() == null)
.collect(Collectors.toList());
}
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findAll()이 전체 데이터를 로딩한 뒤 스트림으로 deletedAt=null만 필터링하고 있어(soft delete 데이터가 많아지면) 불필요한 메모리/DB 부하가 발생한다. Pageable 버전처럼 JPA 쿼리로 deletedAt IS NULL 조건을 내려서 조회하도록 전용 메서드(예: findAllByDeletedAtIsNull())를 추가하는 편이 좋다.

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +11
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;

Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ProductV1Controller에서 org.springframework.web.bind.annotation.* 와일드카드 임포트를 사용하고 있는데, 기존 컨트롤러들은 개별 임포트를 사용한다(예: ExampleV1Controller.java:7-10). 동일한 스타일로 정리해 불필요한 임포트 확장/충돌 가능성을 줄이는 것이 좋다.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +55
@PostMapping
@Override
public ApiResponse<MemberV1Dto.RegisterResponse> register(@Valid @RequestBody MemberV1Dto.RegisterRequest request) {
MemberInfo info = memberFacade.register(
request.loginId(),
request.password(),
request.name(),
request.birthDate(),
request.email()
);
return ApiResponse.success(MemberV1Dto.RegisterResponse.from(info));
}

@GetMapping("/me")
@Override
public ApiResponse<MemberV1Dto.MemberResponse> getMyInfo(
@RequestHeader("X-Loopers-LoginId") String loginId,
@RequestHeader("X-Loopers-LoginPw") String password
) {
MemberInfo info = memberFacade.getMyInfo(loginId, password);
return ApiResponse.success(MemberV1Dto.MemberResponse.from(info));
}

@PutMapping("/password")
@Override
public ApiResponse<Object> changePassword(
@RequestHeader("X-Loopers-LoginId") String loginId,
@RequestHeader("X-Loopers-LoginPw") String currentPassword,
@Valid @RequestBody MemberV1Dto.ChangePasswordRequest request
) {
memberFacade.changePassword(loginId, currentPassword, request.newPassword());
return ApiResponse.success();
}
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Member API(회원가입/내정보 조회/비밀번호 변경)와 관련된 E2E/통합 테스트가 추가되지 않았다. 기존에 ExampleV1ApiE2ETest/ExampleServiceIntegrationTest 패턴이 있으므로, 최소한 성공 케이스와 주요 실패 케이스(중복 loginId, 인증 실패, 비밀번호 규칙 위반 등)를 커버하는 테스트를 추가하는 것이 필요하다.

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +80
@PostMapping
@Override
public ApiResponse<ProductV1Dto.ProductResponse> createProduct(
@Valid @RequestBody ProductV1Dto.CreateRequest request
) {
ProductInfo info = productFacade.createProduct(
request.name(),
request.description(),
request.price(),
request.stock()
);
ProductV1Dto.ProductResponse response = ProductV1Dto.ProductResponse.from(info);
return ApiResponse.success(response);
}

@GetMapping("/{productId}")
@Override
public ApiResponse<ProductV1Dto.ProductResponse> getProduct(
@PathVariable Long productId
) {
ProductInfo info = productFacade.getProduct(productId);
ProductV1Dto.ProductResponse response = ProductV1Dto.ProductResponse.from(info);
return ApiResponse.success(response);
}

@GetMapping
@Override
public ApiResponse<ProductV1Dto.ProductListResponse> getProducts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size
) {
PageRequest pageable = PageRequest.of(page, size);
Page<ProductInfo> productPage = productFacade.getProducts(pageable);
ProductV1Dto.ProductListResponse response = ProductV1Dto.ProductListResponse.from(productPage);
return ApiResponse.success(response);
}

@PutMapping("/{productId}")
@Override
public ApiResponse<ProductV1Dto.ProductResponse> updateProduct(
@PathVariable Long productId,
@Valid @RequestBody ProductV1Dto.UpdateRequest request
) {
ProductInfo info = productFacade.updateProduct(
productId,
request.name(),
request.description(),
request.price(),
request.stock()
);
ProductV1Dto.ProductResponse response = ProductV1Dto.ProductResponse.from(info);
return ApiResponse.success(response);
}

@DeleteMapping("/{productId}")
@Override
public ApiResponse<Object> deleteProduct(
@PathVariable Long productId
) {
productFacade.deleteProduct(productId);
return ApiResponse.success();
}
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Product API(create/get/list/update/delete)와 soft delete 동작(삭제 후 목록/단건에서 제외)이 신규로 들어왔는데, 이를 보장하는 E2E/통합 테스트가 없다. 기존 Example 테스트 패턴처럼 주요 성공/실패 케이스와 soft delete 제외 조건을 검증하는 테스트를 추가하는 것이 필요하다.

Copilot uses AI. Check for mistakes.
Comment thread .coderabbit.yaml
Comment on lines +1 to +5
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

language: "ko-KR"
early_access: false

Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 PR에서 .github/workflows/main.yml이 삭제된 뒤 현재 .github/workflows 디렉터리에 워크플로우가 남아있지 않다. 저장소가 GitHub Actions 기반 CI를 사용 중이었다면 빌드/테스트가 전혀 실행되지 않으므로, 의도된 변경인지 확인하고 최소한 build/test 워크플로우는 유지/대체하는 것이 안전하다.

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +40
if (memberRepository.existsByLoginId(loginId)) {
throw new CoreException(ErrorType.CONFLICT, "이미 사용 중인 로그인 ID입니다.");
}

String encodedPassword = passwordEncoder.encode(password);
MemberModel member = new MemberModel(loginId, encodedPassword, name, birthDate, email);
return memberRepository.save(member);
}
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

register()에서 existsByLoginId 선확인 후 save 하는 방식은 동시 요청에서 레이스가 발생해 DB unique 제약 위반으로 500(INTERNAL_ERROR)로 떨어질 수 있다. save 시점의 unique 제약 위반(DataIntegrityViolationException 등)을 CONFLICT(CoreException)로 변환(또는 ApiControllerAdvice에서 해당 예외를 CONFLICT로 매핑)해 클라이언트가 일관된 응답을 받을 수 있게 처리하는 것이 필요하다.

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +18
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/v1/products")
public class ProductV1Controller implements ProductV1ApiSpec {

private final ProductFacade productFacade;

Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR 제목/설명은 회원 기능(회원가입/내정보/비밀번호 변경) 중심인데, 이 PR에는 Product CRUD API/도메인도 함께 포함되어 있고(.interfaces/api/product, .domain/product 등), GitHub Actions 워크플로우 제거 및 CodeRabbit 설정 추가도 포함되어 범위가 크게 확장돼 있다. 리뷰/릴리즈 단위를 명확히 하기 위해 PR 설명을 변경 범위에 맞게 갱신하거나 PR을 분리하는 편이 좋다.

Copilot uses AI. Check for mistakes.
ukukdin added a commit that referenced this pull request Feb 6, 2026
fix: UserQueryServiceTest 1자 이름 테스트 수정
letter333 added a commit that referenced this pull request Mar 13, 2026
refactor: DTO에 Bean Validation 적용 및 도메인 검증 책임 분리
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants