Member 도메인은 회원의 프로필 조회, 프로필 수정, 회원 검색 API를 제공합니다. 모든 엔드포인트는 MemberController(src/main/java/site/praytogether/pray_together/domain/member/controller/MemberController.java)를 통해 노출됩니다.
- GET /api/v1/members/me - 내 프로필 조회
- PATCH /api/v1/members/me - 내 프로필 수정
- GET /api/v1/members/search - 이름으로 회원 검색
- Handler/Controller: HTTP 요청 수신, DTO 검증, 적절한 HTTP Status/Body 반환 (
MemberController). - UseCase/Application: 도메인 서비스 조합 및 트랜잭션 관리 (
MemberApplicationService). - Domain/Service: 회원 조회/수정 로직 처리 (
MemberService). - Repository/Infra: 회원 데이터 영속화 및 조회 (
MemberRepository).
@PrincipalId파라미터 리졸버가 SecurityContext의PrayTogetherPrincipal에서 memberId를 추출합니다.- Access Token(JWT)은 Authorization 헤더(
Bearer <token>)로 전달되며, 미인증 시 401 Unauthorized.
- 전화번호는
PhoneNumber임베디드 타입으로 관리됩니다. - 하이픈 포함/미포함 입력을 허용하며, 저장 시
XXX-XXXX-XXXX형식으로 정규화됩니다. - 검증 규칙: 한국 휴대폰 번호 형식 (010, 011, 016, 017, 018, 019 + 8자리 숫자).
getSuffix()메서드로 뒤 4자리 추출이 가능합니다.
- 공통 유효성 검증 실패: 400 Bad Request (Bean Validation 메시지 그대로 반환).
MemberNotFoundException: 404 Not Found (MEMBER-001) - "회원 정보를 찾을 수 없습니다."
GET /api/v1/members/me
Authorization: Bearer access token (required)
- 없음
Status Code: 200 OK
Body:
{
"id": "Long",
"name": "String",
"email": "String",
"phoneNumber": "String (nullable, format: XXX-XXXX-XXXX)"
}Example:
{
"id": 123,
"name": "홍길동",
"email": "hong@example.com",
"phoneNumber": "010-1234-5678"
}UseCase Layer (memberApplication.getProfile):
- SecurityContext에서 추출된
memberId를 입력받습니다. MemberService.fetchProfileById(memberId)호출로 회원 프로필 조회.- 존재하지 않으면
MemberNotFoundException(404) 발생.
- 존재하지 않으면
MemberProfile도메인 모델을MemberProfileResponseDTO로 변환.- 전화번호가 없는 경우
phoneNumber필드는null로 반환됩니다.
- Access Token 인증이 완료된 본인의 프로필만 조회할 수 있습니다.
PATCH /api/v1/members/me
Authorization: Bearer access token (required)Content-Type: application/json
{
"name": "String (optional, 1-10 characters)",
"phoneNumber": "String (optional, Korean phone format)"
}Validation Rules:
name: 선택적, 1자 이상 10자 이하 (@Size) - "이름은 1자 이상 10자 이하로 입력해 주세요."phoneNumber: 선택적, 한국 휴대폰 번호 형식 (@Pattern) - "올바른 휴대폰 번호 형식이 아닙니다."- 정규식:
^01[016789]-?\\d{4}-?\\d{4}$ - 하이픈 포함 여부 무관 (예:
010-1234-5678또는01012345678)
- 정규식:
Note:
name과phoneNumber는 모두 선택적(optional) 필드입니다.- 수정하고 싶은 필드만 포함하면 됩니다.
- 빈 JSON
{}을 보내면 아무것도 수정되지 않습니다.
Status Code: 200 OK
Body:
{
"message": "String"
}Example:
{
"message": "프로필을 변경했습니다."
}UseCase Layer (memberApplication.updateProfile):
- SecurityContext에서 추출된
memberId와UpdateProfileRequest를 입력받습니다. - Request를
UpdateProfileCommand도메인 커맨드로 변환.phoneNumber문자열 →PhoneNumberValue Object 변환.- 변환 과정에서 전화번호 형식 검증 및 정규화 수행.
MemberService.fetchById(memberId)로 회원 엔티티 조회.- 존재하지 않으면
MemberNotFoundException(404) 발생.
- 존재하지 않으면
- 부분 업데이트 수행:
command.getName() != null이면member.updateName(name)호출.command.getPhoneNumber() != null이면member.updatePhoneNumber(phoneNumber)호출.
- JPA Dirty Checking으로 변경사항 자동 저장 (@Transactional).
- 성공 메시지 반환.
- Access Token 인증이 완료된 본인의 프로필만 수정할 수 있습니다.
- 이름과 전화번호는 독립적으로 수정 가능합니다 (부분 업데이트).
- 전화번호는 하이픈 포함/미포함 입력을 모두 허용하지만, 저장 시
XXX-XXXX-XXXX형식으로 정규화됩니다. - 이메일과 비밀번호는 이 API로 수정할 수 없습니다.
GET /api/v1/members/search
name: String (required) - 검색할 회원 이름 (부분 일치)
Authorization: Bearer access token (required)
Status Code: 200 OK
Body:
{
"members": [
{
"id": "Long",
"name": "String",
"phoneNumberSuffix": "String (nullable, 전화번호 뒤 4자리)"
}
]
}Example:
{
"members": [
{
"id": 123,
"name": "홍길동",
"phoneNumberSuffix": "5678"
},
{
"id": 456,
"name": "홍길순",
"phoneNumberSuffix": null
}
]
}Note:
- 전화번호 전체가 아닌 뒤 4자리만 반환됩니다 (개인정보 보호).
- 전화번호를 등록하지 않은 회원의 경우
phoneNumberSuffix는null입니다.
UseCase Layer (memberApplication.searchMembers):
- SecurityContext에서 추출된
memberId(인증 확인용)와 검색어searchName을 입력받습니다. - 검색어를
SearchQueryMember도메인 모델로 변환. MemberService.searchByName(queryMember)호출.- Repository에서
findByNameContaining(name)쿼리 실행 (부분 일치 검색). SearchResultMember리스트 반환 (id, name, phoneNumber).
- Repository에서
SearchResultMembers도메인 모델을SearchMemberResponseDTO로 변환.phoneNumber.getSuffix()를 호출해 뒤 4자리만 추출.
- 검색 결과 리스트 반환.
- Access Token 인증이 완료된 회원만 검색할 수 있습니다.
- 본인 외의 회원도 검색 가능합니다 (공개 검색).
- 부분 일치 검색: 입력한 이름이 포함된 모든 회원을 반환합니다.
- 예: "홍" 검색 시 "홍길동", "홍길순", "김홍철" 모두 반환.
- 대소문자 구분: 데이터베이스 설정에 따라 다를 수 있습니다.
- 빈 검색어: 현재 구현상 빈 문자열도 허용되며, 모든 회원을 반환할 수 있습니다.
{
name: String (optional, @Size(min=1, max=10))
phoneNumber: String (optional, @Pattern(regexp="^01[016789]-?\\d{4}-?\\d{4}$"))
}
Validation Messages:
- name: "이름은 1자 이상 10자 이하로 입력해 주세요."
- phoneNumber: "올바른 휴대폰 번호 형식이 아닙니다."
{
id: Long
name: String
email: String
phoneNumber: String (nullable)
}
{
members: Array<SearchMemberDto>
}
SearchMemberDto:
{
id: Long
name: String
phoneNumberSuffix: String (nullable, 뒤 4자리)
}
{
message: String
}
- 프로필 조회: 인증된 본인만 자신의 프로필 조회 가능
- 프로필 수정: 인증된 본인만 자신의 프로필 수정 가능
- 회원 검색: 인증된 모든 회원이 다른 회원 검색 가능
- 이름 변경: 1자 이상 10자 이하로 제한됩니다.
- 전화번호 변경: 한국 휴대폰 번호 형식만 허용되며, 저장 시
XXX-XXXX-XXXX형식으로 정규화됩니다. - 이메일 불변: 이메일은 회원 가입 후 변경할 수 없습니다 (고유 식별자).
- 부분 업데이트: 프로필 수정 시 제공된 필드만 업데이트되며, 제공되지 않은 필드는 기존 값을 유지합니다.
- 이름 검증:
- 길이: 1자 이상 10자 이하
- 공백만으로는 구성될 수 없음
- 전화번호 검증:
- 형식:
01[016789]-?\\d{4}-?\\d{4}(하이픈 선택적) - 정규화 후 형식:
XXX-XXXX-XXXX - Value Object 생성 시 자동 검증 및 정규화 수행
- 형식:
- 검색어 검증:
- 현재 빈 문자열도 허용됨 (개선 가능)
- 동일한 엔드포인트/경로를 유지합니다.
- Request validation: struct tags를 사용해 검증합니다.
- JSON 바인딩 및 응답 직렬화를 처리합니다.
- 인증 미들웨어에서 memberId를 추출해 handler에 전달합니다.
MemberApplicationService의 역할을 Go service로 분리합니다.- 트랜잭션 경계를 관리합니다 (@Transactional).
- Request DTO → Domain Command 변환을 담당합니다.
- 부분 업데이트 로직 (nil 체크)을 유지합니다.
- PhoneNumber Value Object:
- 전화번호 검증 및 정규화 로직을 구조체 메서드로 구현합니다.
Normalize(),Validate(),GetSuffix()메서드 제공.
- Member Entity:
UpdateName(),UpdatePhoneNumber()메서드로 불변성을 보장합니다.
- 부분 업데이트: Optional 필드 처리를 위해 포인터 타입 사용을 고려합니다.
- 프로필 조회를 위한 projection 쿼리를 구현합니다.
- 이름 검색:
LIKE '%name%'또는 full-text search를 사용합니다. - 전화번호 뒤 4자리 추출 로직을 repository 또는 mapper에서 처리합니다.
- 인증: JWT 토큰 검증 및 memberId 추출.
- 로깅: API 시작/종료 로그 (
[API] 내 정보 조회 시작/종료). - 에러 처리: 일관된 에러 응답 포맷 유지.
- 개인정보 보호: 검색 시 전화번호 전체가 아닌 뒤 4자리만 노출.
- 프로필 조회: 존재하지 않는 memberId로 조회 시 404 응답 확인.
- 프로필 수정:
- 부분 업데이트 검증 (name만, phoneNumber만, 둘 다, 둘 다 없음).
- 전화번호 형식 검증 (하이픈 포함/미포함 모두 성공).
- 잘못된 전화번호 형식으로 400 응답 확인.
- 회원 검색:
- 부분 일치 검색이 정상 작동하는지 확인.
- 전화번호 뒤 4자리만 반환되는지 확인.
- 전화번호가 없는 회원의 경우 null 처리 확인.
- 인증: 토큰 없이 호출 시 401 응답 확인.
-
개인정보 보호:
- 회원 검색 시 전화번호 전체를 노출하지 않고 뒤 4자리만 반환합니다.
- 이메일은 검색 결과에 포함되지 않습니다.
-
접근 제어:
- 프로필 조회/수정은 본인만 가능합니다.
- 회원 검색은 인증된 모든 사용자가 가능합니다.
-
데이터 검증:
- 전화번호는 Value Object를 통해 도메인 레벨에서 검증됩니다.
- Controller 레벨의 Bean Validation과 이중 검증 구조를 가집니다.
-
Rate Limiting:
- 현재 구현되어 있지 않으며, 외부 API Gateway나 미들웨어에서 처리해야 합니다.
- 특히 회원 검색 API는 남용 방지를 위해 rate limiting 적용을 권장합니다.