Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package konkuk.chacall.domain.user.application;

import konkuk.chacall.domain.user.domain.model.User;
import konkuk.chacall.domain.user.domain.repository.UserRepository;
import konkuk.chacall.domain.user.presentation.dto.request.UpdateUserInfoRequest;
import konkuk.chacall.domain.user.presentation.dto.response.UserResponse;
import konkuk.chacall.global.common.exception.EntityNotFoundException;
import konkuk.chacall.global.common.exception.code.ErrorCode;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService {

private final UserRepository userRepository;

public UserResponse getUserInfo(Long userId) {
return userRepository.findById(userId)
.map(UserResponse::from)
.orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_NOT_FOUND));
}

@Transactional
public void updateUserInfo(Long userId, UpdateUserInfoRequest request) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_NOT_FOUND));

user.update(request.name(), request.profileImageUrl(), request.email(), request.gender(), request.termAgreed());
}
}
16 changes: 16 additions & 0 deletions src/main/java/konkuk/chacall/domain/user/domain/model/Gender.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package konkuk.chacall.domain.user.domain.model;

import konkuk.chacall.global.common.exception.DomainRuleException;
import konkuk.chacall.global.common.exception.code.ErrorCode;
import lombok.Getter;

import java.util.Arrays;

@Getter
public enum Gender {
M("남성"), F("여성");
Expand All @@ -11,4 +15,16 @@ public enum Gender {
Gender(String value) {
this.value = value;
}

public static Gender from(String value) {
return Arrays.stream(Gender.values())
.filter(genderVal -> genderVal.getValue().equals(value.trim()))
.findFirst()
.orElseThrow(
() -> new DomainRuleException(ErrorCode.USER_GENDER_MISMATCH,
new IllegalArgumentException(
String.format("존재하지 않는 성별입니다. value: %s", value)
))
);
}
}
13 changes: 13 additions & 0 deletions src/main/java/konkuk/chacall/domain/user/domain/model/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,26 @@ public class User extends BaseEntity {
@Column(length = 15, nullable = false)
private Role role;

// 약관 동의 여부 컬럼
@Column(nullable = false)
private boolean termsAgreed;

Comment on lines +40 to +43

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

기존 데이터가 있는 테이블에 NOT NULL 컬럼 추가 시 마이그레이션 필요

nullable=false boolean 컬럼 추가는 운영 DB에서 실패할 수 있습니다. 기본값/백필 전략을 포함한 마이그레이션을 선반영하세요.

예시 SQL(선반영 후 제약 강화):

-- 1) 컬럼 추가(임시 default)
ALTER TABLE users ADD COLUMN terms_agreed BOOLEAN DEFAULT FALSE;

-- 2) 기존 로우 백필(필요 시 조건부 업데이트)
UPDATE users SET terms_agreed = FALSE WHERE terms_agreed IS NULL;

-- 3) NOT NULL 제약 강화 및 기본값 제거(선택)
ALTER TABLE users ALTER COLUMN terms_agreed SET NOT NULL;
ALTER TABLE users ALTER COLUMN terms_agreed DROP DEFAULT;
🤖 Prompt for AI Agents
src/main/java/konkuk/chacall/domain/user/domain/model/User.java lines 40-43: 현재
nullable=false로 boolean 컬럼을 바로 추가하면 운영 DB 마이그레이션에서 실패할 수 있으니 마이그레이션을 선행해 처리하세요;
DB 마이그레이션 스크립트를 만들어(1) terms_agreed 컬럼을 DEFAULT FALSE로 추가하고(2) 기존 로우에 대해 NULL인
값들을 FALSE로 백필한 다음(3) ALTER로 NOT NULL 제약을 설정하고 필요하면 DEFAULT를 제거하는 단계를 적용하고,
애플리케이션 코드와 마이그레이션이 일관되도록 배포 전 로컬/스테이징에서 검증하세요.

public static User createNewUser(String name, String profileImageUrl, String kakaoId, String email) {
return User.builder()
.name(name)
.profileImageUrl(profileImageUrl)
.kakaoId(kakaoId)
.email(email)
.termsAgreed(false)
.role(Role.NON_SELECTED)
.build();
}

public void update(String name, String profileImageUrl, String email, String genderStr, boolean termsAgreed) {
this.name = name;
this.profileImageUrl = profileImageUrl;
this.email = email;
this.gender = Gender.from(genderStr);
this.termsAgreed = termsAgreed;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package konkuk.chacall.domain.user.presentation;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import konkuk.chacall.domain.user.application.UserService;
import konkuk.chacall.domain.user.presentation.dto.request.UpdateUserInfoRequest;
import konkuk.chacall.domain.user.presentation.dto.response.UserResponse;
import konkuk.chacall.global.common.annotation.ExceptionDescription;
import konkuk.chacall.global.common.annotation.UserId;
import konkuk.chacall.global.common.dto.BaseResponse;
import konkuk.chacall.global.common.swagger.SwaggerResponseDescription;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@Tag(name = "User API", description = "전체 사용자(마이페이지) 관련 API")
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {

private final UserService userService;

@Operation(summary = "[마이페이지] 회원 정보 조회", description = "사용자(고객)의 정보를 조회합니다. (사장님, 일반유저 무관)")
@ExceptionDescription(SwaggerResponseDescription.GET_USER_INFO)
@GetMapping("/me")
public BaseResponse<UserResponse> getUserInfo(
@Parameter(hidden = true) @UserId final Long userId
) {
return BaseResponse.ok(userService.getUserInfo(userId));
}

@Operation(summary = "[마이페이지] 회원 정보 수정", description = "사용자(고객)의 정보를 수정합니다. (사장님, 일반유저 무관)")
@ExceptionDescription(SwaggerResponseDescription.UPDATE_USER_INFO)
@PutMapping("/me")
public BaseResponse<Void> updateUserInfo(
@RequestBody @Valid final UpdateUserInfoRequest request,
@Parameter(hidden = true) @UserId final Long userId
) {
userService.updateUserInfo(userId, request);
Comment thread
buzz0331 marked this conversation as resolved.

return BaseResponse.ok(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package konkuk.chacall.domain.user.presentation.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

@Schema(description = "사용자 정보 수정 요청 DTO")
public record UpdateUserInfoRequest(

@Schema(description = "프로필 이미지 URL", example = "https://example.com/profile.jpg")
@NotBlank(message = "프로필 이미지 URL은 비어 있을 수 없습니다.")
String profileImageUrl,

@Schema(description = "사용자 이름", example = "홍길동")
@NotBlank(message = "이름은 비어 있을 수 없습니다.")
String name,

@Schema(description = "사용자 이메일", example = "chacall@kokuk.ac.kr")
@NotBlank(message = "이메일은 비어 있을 수 없습니다.")
@Email(message = "올바른 이메일 형식이 아닙니다.")
String email,

@Schema(description = "사용자 성별", example = "남성")
@NotBlank(message = "성별은 비어 있을 수 없습니다.")
String gender,

@Schema(description = "약관 동의 여부", example = "true")
@NotNull(message = "약관 동의 여부는 비어 있을 수 없습니다.")
Boolean termAgreed
) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package konkuk.chacall.domain.user.presentation.dto.response;

import konkuk.chacall.domain.user.domain.model.User;

public record UserResponse(
String profileImageUrl,
String name,
String email,
String gender,
boolean termAgreed
) {
Comment on lines +5 to +11

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

응답 필드명 일관성: termAgreed → termsAgreed로 변경 권장

도메인(User.isTermsAgreed()), PR 설명(컬럼명 termsAgreed)과 응답 DTO 필드(termAgreed)가 불일치합니다. API 계약 혼동을 막기 위해 복수형으로 통일하세요.

아래 diff 적용 후, 해당 접근자(termsAgreed())를 사용하는 컨트롤러/테스트/문서도 함께 갱신해 주세요.

 public record UserResponse(
         String profileImageUrl,
         String name,
         String email,
         String gender,
-        boolean termAgreed
+        boolean termsAgreed
 ) {

검증 스크립트:


🏁 Script executed:

#!/bin/bash
# 'termAgreed' 잔존 사용처 점검
rg -nP -C2 '\btermAgreed\b|\.termAgreed\(\)'

Length of output: 1782


필드명 일관성 확보 — termAgreed → termsAgreed로 전파 (필수)

아래 파일들이 아직 termAgreed를 사용중이므로 UserResponse 필드명 변경 시 모든 참조(요청 DTO, 서비스 호출, 테스트, 문서 등)를 일괄 갱신해야 함.

  • 수정 대상(발견된 참조):
    • src/main/java/konkuk/chacall/domain/user/presentation/dto/response/UserResponse.java — boolean termAgreed → boolean termsAgreed (record 생성자 및 from(User) 매핑)
    • src/main/java/konkuk/chacall/domain/user/presentation/dto/request/UpdateUserInfoRequest.java — boolean termAgreed → boolean termsAgreed
    • src/main/java/konkuk/chacall/domain/user/application/UserService.java — request.termAgreed() → request.termsAgreed() (user.update(...) 호출부)

원본 diff(적용 필요):

 public record UserResponse(
         String profileImageUrl,
         String name,
         String email,
         String gender,
-        boolean termAgreed
+        boolean termsAgreed
 ) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public record UserResponse(
String profileImageUrl,
String name,
String email,
String gender,
boolean termAgreed
) {
public record UserResponse(
String profileImageUrl,
String name,
String email,
String gender,
boolean termsAgreed
) {
🤖 Prompt for AI Agents
In
src/main/java/konkuk/chacall/domain/user/presentation/dto/response/UserResponse.java
around lines 5 to 11, rename the record field and constructor parameter
termAgreed to termsAgreed, update any internal mapping (e.g., from(User)
factory/mapping) to call user.isTermsAgreed() or equivalent, and then propagate
this rename to referenced files:
src/main/java/konkuk/chacall/domain/user/presentation/dto/request/UpdateUserInfoRequest.java
(boolean termAgreed → boolean termsAgreed and accessor rename), and
src/main/java/konkuk/chacall/domain/user/application/UserService.java
(request.termAgreed() → request.termsAgreed() where user.update(...) is
invoked); run and update related tests and any docs that reference termAgreed to
use termsAgreed.

public static UserResponse from(User user) {
return new UserResponse(
user.getProfileImageUrl(),
user.getName(),
user.getEmail(),
user.getGender() == null ? null : user.getGender().getValue(),
user.isTermsAgreed()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public enum ErrorCode implements ResponseCode {
USER_NOT_FOUND(HttpStatus.NOT_FOUND, 60001, "사용자를 찾을 수 없습니다."),
USER_ALREADY_EXISTS(HttpStatus.CONFLICT, 60002, "이미 존재하는 사용자입니다."),
USER_NICKNAME_DUPLICATION(HttpStatus.CONFLICT, 60003, "이미 존재하는 닉네임입니다."),
USER_GENDER_MISMATCH(HttpStatus.BAD_REQUEST, 60004, "일치하는 성별이 없습니다."),

/**
* BankAccount
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class AuthController {
*/
@Operation(summary = "JWT Access Token 발급", description = "loginTokenKey를 통해 JWT Access Token을 발급받습니다." +
" 카카오 소셜 로그인 직후 리다이렉트 url의 쿼리 파라미터에서 loginTokenKey를 꺼내서 요청해주세요.")
@ExceptionDescription(SwaggerResponseDescription.LOGOUT)
@ExceptionDescription(SwaggerResponseDescription.ISSUE_TOKEN)
@PostMapping("/token")
public BaseResponse<AuthTokenResponse> getToken(
@RequestBody @Valid AuthTokenRequest request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@ public enum SwaggerResponseDescription {

))),
ISSUE_TOKEN(new LinkedHashSet<>(Set.of(
AUTH_INVALID_LOGIN_TOKEN_KEY,
AUTH_INVALID_LOGIN_TOKEN_KEY
))),

// User
GET_USER_INFO(new LinkedHashSet<>(Set.of(
USER_NOT_FOUND
))),
UPDATE_USER_INFO(new LinkedHashSet<>(Set.of(
USER_NOT_FOUND
))),

Expand Down