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
Expand Up @@ -104,7 +104,6 @@ public User processLogin(String code) {
).orElseGet(() ->
userRepository.save(User.builder()
.email(kakaoUserInfo.getKakaoAccount().getEmail())
.nickname(kakaoUserInfo.getProperties().getNickname())
.profileImageUrl(
kakaoUserInfo.getKakaoAccount()
.getProfile()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.sossbar.global.common.code.SuccessCode;
import com.sossbar.global.common.template.ApiResTemplate;
import com.sossbar.global.common.template.SwaggerApiResTemplate;
import com.sossbar.user.dto.request.UserOnboardingReqDto;
import com.sossbar.user.dto.request.UserInfoUpdateReqDto;
import com.sossbar.user.dto.response.UserInfoResDto;
import com.sossbar.user.service.UserService;
Expand All @@ -23,35 +22,25 @@
@RequiredArgsConstructor
@RequestMapping("/api/v1/users")
@SwaggerApiResTemplate
@Tag(name = "MyPage API", description = "내 프로필 관련 API")
@Tag(name = "User API", description = "사용자 관련 API - MyPage")
public class UserController {

private final UserService userService;

@Operation(summary = "사용자 정보 추가 입력", description = "카카오 로그인을 완료한 사용자가 최초 1회 추가 정보(이름, 한 줄 소개, 프로필 이미지)를 입력합니다." +
@Operation(summary = "사용자 정보 추가 입력", description = "카카오 로그인을 완료한 사용자가 최초 1회 추가 정보(실명, 한 줄 소개, 프로필 이미지)를 입력합니다." +
"<br> 이미지를 빈 값으로 보낼 시 url은 null로 저장됨")
@PostMapping(value = "/onboarding", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ApiResTemplate<UserInfoResDto> saveUserInfo(Principal principal,
@Valid @RequestPart("onboarding") UserOnboardingReqDto userOnboardingReqDto,
@Valid @RequestPart("onboarding") UserInfoUpdateReqDto userInfoUpdateReqDto,
@RequestPart(value = "profileImage", required = false) MultipartFile profileImage) {
UserInfoResDto userInfoResDto = userService.onboarding(principal, userOnboardingReqDto, profileImage);
UserInfoResDto userInfoResDto = userService.onboarding(principal, userInfoUpdateReqDto, profileImage);
return ApiResTemplate.successResponse(SuccessCode.SUCCESS, userInfoResDto);
}

@Operation(summary = "내 정보 조회", description = "로그인한 사용자가 자신의 프로필 정보를 조회합니다.")
@Operation(summary = "내 정보 조회", description = "로그인한 사용자가 자신의 계정 정보를 조회합니다. (본인만 조회 가능, 이메일 포함)")
@GetMapping("/profile")
public ApiResTemplate<UserInfoResDto> getUserInfo(Principal principal) {
UserInfoResDto userInfoResDto = userService.getUserInfo(principal);
return ApiResTemplate.successResponse(SuccessCode.GET_SUCCESS, userInfoResDto);
}

@Operation(summary = "내 정보 수정", description = "로그인한 사용자가 자신의 프로필 정보를 수정합니다. (닉네임, 한 줄 소개, 프로필 이미지) " +
"<br> 이미지를 빈 값으로 보낼 시 기존 프로필 이미지 유지")
@PatchMapping(value = "/profile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ApiResTemplate<UserInfoResDto> updateUserInfo(Principal principal,
@Valid @RequestPart("info") UserInfoUpdateReqDto userInfoUpdateReqDto,
@RequestPart(value = "profileImage", required = false) MultipartFile profileImage) {
UserInfoResDto userInfoResDto = userService.updateUserInfo(principal, userInfoUpdateReqDto, profileImage);
return ApiResTemplate.successResponse(SuccessCode.UPDATE_SUCCESS, userInfoResDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.sossbar.user.controller;

import com.sossbar.global.common.code.SuccessCode;
import com.sossbar.global.common.template.ApiResTemplate;
import com.sossbar.global.common.template.SwaggerApiResTemplate;
import com.sossbar.user.dto.request.UserInfoUpdateReqDto;
import com.sossbar.user.dto.response.UserInfoResDto;
import com.sossbar.user.dto.response.UserProfileInfoResDto;
import com.sossbar.user.service.UserProfileService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.security.Principal;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/users")
@SwaggerApiResTemplate
@Tag(name = "User Profile API", description = "사용자 프로필 관련 API")
public class UserProfileController {

private final UserProfileService userProfileService;

@Operation(summary = "사용자 프로필 정보 조회", description = "로그인한 사용자가 userId로 사용자 프로필 정보(실명, 프로필 사진, 한 줄 소개)를 조회합니다.")
@GetMapping("/profile/{userId}")
public ApiResTemplate<UserProfileInfoResDto> getUserProfile(@PathVariable("userId") Long userId) {
UserProfileInfoResDto userProfileInfoResDto = userProfileService.getUserProfile(userId);
return ApiResTemplate.successResponse(SuccessCode.GET_SUCCESS, userProfileInfoResDto);
}

@Operation(summary = "내 프로필 수정", description = "로그인한 사용자가 자신의 프로필 정보를 수정합니다. (실명, 한 줄 소개, 프로필 이미지)" +
"<br> 이미지를 빈 값으로 보낼 시 기존 프로필 이미지 유지")
@PatchMapping(value = "/profile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
Comment thread
ttttkii913 marked this conversation as resolved.
public ApiResTemplate<UserInfoResDto> updateUserInfo(Principal principal,
@Valid @RequestPart("info") UserInfoUpdateReqDto userInfoUpdateReqDto,
@RequestPart(value = "profileImage", required = false) MultipartFile profileImage) {
UserInfoResDto userInfoResDto = userProfileService.updateUserInfo(principal, userInfoUpdateReqDto, profileImage);
return ApiResTemplate.successResponse(SuccessCode.UPDATE_SUCCESS, userInfoResDto);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
import jakarta.validation.constraints.Size;

public record UserInfoUpdateReqDto(
@NotBlank
@Size(min = 2, max = 20, message = "닉네임은 2자 이상 20자 이하로 입력해 주세요.")
String nickname,
@NotBlank(message = "실명만 입력해 주세요.")
@Size(min = 2, max = 20, message = "이름은 2자 이상 20자 이하로 입력해 주세요.")
String username,

@NotBlank
@Size(max = 100, message = "한 줄 소개는 100자 이하로 입력해 주세요.")
String bio
) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
public record UserInfoResDto(
Long userId,
String username,
String nickname,
String email,
String bio,
String profileImageUrl,
Expand All @@ -18,7 +17,6 @@ public static UserInfoResDto from(User user) {
return UserInfoResDto.builder()
.userId(user.getId())
.username(user.getUsername())
.nickname(user.getNickname())
.email(user.getEmail())
.bio(user.getBio())
.profileImageUrl(user.getProfileImageUrl())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.sossbar.user.dto.response;

import com.sossbar.user.entity.User;
import lombok.Builder;

@Builder
public record UserProfileInfoResDto(
Long userId,
String username,
String bio,
String profileImageUrl
) {
public static UserProfileInfoResDto from(User user) {
return UserProfileInfoResDto.builder()
.userId(user.getId())
.username(user.getUsername())
.bio(user.getBio())
.profileImageUrl(user.getProfileImageUrl())
.build();
}
}
25 changes: 12 additions & 13 deletions SossBar/src/main/java/com/sossbar/user/entity/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.sossbar.global.common.template.BaseTimeEntity;
import com.sossbar.user.dto.request.UserInfoUpdateReqDto;
import com.sossbar.user.dto.request.UserOnboardingReqDto;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
Expand All @@ -18,7 +17,6 @@ public class User extends BaseTimeEntity {
@Column(name = "user_id")
private Long id;
private String username;
private String nickname;

@Column(unique = true, nullable = false)
private String email;
Expand All @@ -30,26 +28,27 @@ public class User extends BaseTimeEntity {
private String refreshToken;

@Builder
public User(String username, String nickname, String email, String bio, String profileImageUrl, UserType userType, String refreshToken) {
public User(String username, String email, String bio, String profileImageUrl, UserType userType, String refreshToken) {
this.username = username;
this.nickname = nickname;
this.email = email;
this.bio = bio;
this.profileImageUrl = profileImageUrl;
this.userType = userType;
this.refreshToken = refreshToken;
}

public void onboarding(UserOnboardingReqDto userInfoSaveReqDto, String profileImageUrl) {
this.username = userInfoSaveReqDto.username();
this.bio = userInfoSaveReqDto.bio();
this.profileImageUrl = profileImageUrl;
}

public void updateUserInfo(UserInfoUpdateReqDto userInfoUpdateReqDto, String profileImageUrl) {
this.nickname = userInfoUpdateReqDto.nickname();
this.bio = userInfoUpdateReqDto.bio();
this.profileImageUrl = profileImageUrl;
if (userInfoUpdateReqDto.username() != null) {
this.username = userInfoUpdateReqDto.username();
}

if (userInfoUpdateReqDto.bio() != null) {
this.bio = userInfoUpdateReqDto.bio();
}

if (profileImageUrl != null) {
this.profileImageUrl = profileImageUrl;
}
}

public void saveRefreshToken(String refreshToken) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,4 @@
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
boolean existsByNickname(String nickname);
boolean existsByNicknameAndIdNot(String nickname, Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.sossbar.user.service;

import com.sossbar.global.common.code.ErrorCode;
import com.sossbar.global.common.exception.BusinessException;
import com.sossbar.global.config.S3Service;
import com.sossbar.user.dto.request.UserInfoUpdateReqDto;
import com.sossbar.user.dto.response.UserInfoResDto;
import com.sossbar.user.dto.response.UserProfileInfoResDto;
import com.sossbar.user.entity.User;
import com.sossbar.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.security.Principal;

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

private final UserRepository userRepository;
private final S3Service s3Service;

// 프로필 페이지 - 내 프로필 수정(실명, 한 줄 소개, 프로필 이미지)
@Transactional
public UserInfoResDto updateUserInfo(Principal principal, UserInfoUpdateReqDto userInfoUpdateReqDto, MultipartFile profileImage) {
Long id = Long.parseLong(principal.getName());
User user = getUserById(id);

// 프로필 이미지 수정 처리 (기존 이미지 삭제 후 새 이미지 업로드)
String newProfileImageUrl = user.getProfileImageUrl();

if (profileImage != null && !profileImage.isEmpty()) {
// 기존 이미지가 존재한다면 S3에서 삭제
if (newProfileImageUrl != null && !newProfileImageUrl.isBlank()) {
s3Service.deleteFile(newProfileImageUrl);
}
// 새 이미지 업로드
newProfileImageUrl = s3Service.uploadFile(profileImage, "sossbar/profile");
}

user.updateUserInfo(userInfoUpdateReqDto, newProfileImageUrl);

return UserInfoResDto.from(user);
}

// 프로필 페이지 - 사용자 프로필 조회, userId로 구분
public UserProfileInfoResDto getUserProfile(Long userId) {
User user = getUserById(userId);

return UserProfileInfoResDto.from(user);
}

// entity 찾는 공통 메소드
private User getUserById(Long userId) {
return userRepository.findById(userId).orElseThrow(
() -> new BusinessException(ErrorCode.USER_NOT_FOUND_EXCEPTION
, ErrorCode.USER_NOT_FOUND_EXCEPTION.getMessage() + userId));
}
Comment thread
ttttkii913 marked this conversation as resolved.
}
43 changes: 4 additions & 39 deletions SossBar/src/main/java/com/sossbar/user/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.sossbar.global.common.code.ErrorCode;
import com.sossbar.global.common.exception.BusinessException;
import com.sossbar.global.config.S3Service;
import com.sossbar.user.dto.request.UserOnboardingReqDto;
import com.sossbar.user.dto.request.UserInfoUpdateReqDto;
import com.sossbar.user.dto.response.UserInfoResDto;
import com.sossbar.user.entity.User;
Expand All @@ -23,9 +22,9 @@ public class UserService {
private final UserRepository userRepository;
private final S3Service s3Service;

// 온보딩 - 사용자 추가 정보 입력 (이름, 한 줄 소개, 프로필 이미지)
// 온보딩 - 사용자 추가 정보 입력 (실명, 한 줄 소개, 프로필 이미지)
@Transactional
public UserInfoResDto onboarding(Principal principal, UserOnboardingReqDto userOnboardingReqDto, MultipartFile profileImage) {
public UserInfoResDto onboarding(Principal principal, UserInfoUpdateReqDto userInfoUpdateReqDto, MultipartFile profileImage) {
Long id = Long.parseLong(principal.getName());
User user = getUserById(id);

Expand All @@ -43,53 +42,19 @@ public UserInfoResDto onboarding(Principal principal, UserOnboardingReqDto userO
profileImageUrl = s3Service.uploadFile(profileImage, "sossbar/profile");
}

user.onboarding(userOnboardingReqDto, profileImageUrl);
user.updateUserInfo(userInfoUpdateReqDto, profileImageUrl);

return UserInfoResDto.from(user);
}

// 마이페이지 - 내 프로필 조회
// 마이페이지 - 내 계정 정보 조회(실명, 이메일)
public UserInfoResDto getUserInfo(Principal principal) {
Long id = Long.parseLong(principal.getName());
User user = getUserById(id);

return UserInfoResDto.from(user);
}

// 마이페이지 - 내 프로필 수정
@Transactional
public UserInfoResDto updateUserInfo(Principal principal, UserInfoUpdateReqDto userInfoUpdateReqDto, MultipartFile profileImage) {
Long id = Long.parseLong(principal.getName());
User user = getUserById(id);

String newNickname = userInfoUpdateReqDto.nickname();

// nickname 중복 체크 - 자신 제외
if (newNickname != null &&
userRepository.existsByNicknameAndIdNot(newNickname, user.getId())) {
throw new BusinessException(
ErrorCode.VALIDATION_ERROR,
"이미 사용 중인 닉네임입니다."
);
}

// 프로필 이미지 수정 처리 (기존 이미지 삭제 후 새 이미지 업로드)
String newProfileImageUrl = user.getProfileImageUrl();

if (profileImage != null && !profileImage.isEmpty()) {
// 기존 이미지가 존재한다면 S3에서 삭제
if (newProfileImageUrl != null && !newProfileImageUrl.isBlank()) {
s3Service.deleteFile(newProfileImageUrl);
}
// 새 이미지 업로드
newProfileImageUrl = s3Service.uploadFile(profileImage, "sossbar/profile");
}

user.updateUserInfo(userInfoUpdateReqDto, newProfileImageUrl);

return UserInfoResDto.from(user);
}

// entity 찾는 공통 메소드
private User getUserById(Long userId) {
return userRepository.findById(userId).orElseThrow(
Expand Down