Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: 요청 Dto 검증 과정 추가 (#47) #49

Merged
merged 15 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
50f7883
:sparkles: Feat: 캐싱 Key를 위한 수정/삭제 관련 요청 dto 및 헤더 수정 (#44)
win-luck Oct 31, 2023
97ed35b
:sparkles: Feat: CreateFoodDto에 date 속성 추가 (#44)
win-luck Oct 31, 2023
dbe6078
:sparkles: Feat: FoodService 캐싱 적용 (#44)
win-luck Oct 31, 2023
25ba044
:white_check_mark: Refactor: 음식/즐찾음식 삭제 기능 유효성검사 보강 (#44)
win-luck Oct 31, 2023
8042863
:white_check_mark: Refactor: 테스트코드에 유효성검사 반영 (#44)
win-luck Oct 31, 2023
96c3743
:package: Chore: Dto 요청/응답으로 패키지 분리 (#47)
win-luck Nov 2, 2023
d410bc7
:art: Chore: validation 의존성 추가 및 응답객체 수정, 예외처리 보강 (#47)
win-luck Nov 2, 2023
58d55d5
:adhesive_bandage: Fix: Jwt 토큰 반환시간 6시간으로 연장 (#47)
win-luck Nov 2, 2023
1f5633e
:safety_vest: Refactor: Auth, User Controller @Valid 추가 (#47)
win-luck Nov 2, 2023
727e612
:recycle: Refactor: ValidCheck 관련 메시지 통합 관리 클래스 생성 (#47)
win-luck Nov 2, 2023
854ace8
:recycle: Refactor: User 패키지 RequestDto 유효성검사 구현 (#47)
win-luck Nov 2, 2023
d1e3e07
:bug: Fix: ExceptionHandling Status 누락되던 버그 수정 (#47)
win-luck Nov 2, 2023
4fd7d40
:white_check_mark: Test: UserControllerTest 유효성검사 반영 (#47)
win-luck Nov 2, 2023
8c325f0
:safety_vest: Refactor: FoodController @Valid 추가 (#47)
win-luck Nov 2, 2023
a94e464
:recycle: Refactor: Food 패키지 RequestDto 유효성검사 구현 (#47)
win-luck Nov 2, 2023
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
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ dependencies {

// Redis dependency
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

// Validation dependency
implementation 'org.springframework.boot:spring-boot-starter-validation'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public String createToken(String userPk) {
return Jwts.builder()
.setClaims(claims) // 정보 저장
.setIssuedAt(now) // 토큰 발행 시간 정보
.setExpiration(new Date(now.getTime() + (30 * 60 * 1000L))) // 토큰 유효시각 설정 (30분)
.setExpiration(new Date(now.getTime() + (360 * 60 * 1000L))) // 토큰 유효시각 설정 (360분)
.signWith(SignatureAlgorithm.HS256, secretKey) // 암호화 알고리즘과, secret 값
.compact();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.diareat.diareat.auth.component.JwtTokenProvider;
import com.diareat.diareat.auth.service.KakaoAuthService;
import com.diareat.diareat.user.dto.JoinUserDto;
import com.diareat.diareat.user.dto.request.JoinUserDto;
import com.diareat.diareat.user.service.UserService;
import com.diareat.diareat.util.api.ApiResponse;
import com.diareat.diareat.util.api.ResponseCode;
Expand All @@ -11,6 +11,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.HashMap;

@Api(tags = "2. Auth")
Expand All @@ -36,7 +37,7 @@ public ApiResponse<HashMap<Long, String>> authCheck(@RequestHeader String access
// 회원가입 (성공 시 Jwt 토큰 발급)
@Operation(summary = "[회원가입] 회원가입 및 토큰 발급", description = "신규 회원가입을 처리하고, 회원가입 성공 시 Jwt 토큰을 발급합니다.")
@PostMapping("/join")
public ApiResponse<HashMap<Long, String>> saveUser(JoinUserDto joinUserDto) {
public ApiResponse<HashMap<Long, String>> saveUser(@Valid @RequestBody JoinUserDto joinUserDto) {
Long userId = userService.saveUser(kakaoAuthService.createUserDto(joinUserDto));
HashMap<Long, String> map = new HashMap<>();
map.put(userId, jwtTokenProvider.createToken(userId.toString()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import com.diareat.diareat.auth.component.KakaoUserInfo;
import com.diareat.diareat.auth.dto.KakaoUserInfoResponse;
import com.diareat.diareat.user.domain.User;
import com.diareat.diareat.user.dto.CreateUserDto;
import com.diareat.diareat.user.dto.JoinUserDto;
import com.diareat.diareat.user.dto.request.CreateUserDto;
import com.diareat.diareat.user.dto.request.JoinUserDto;
import com.diareat.diareat.user.repository.UserRepository;
import com.diareat.diareat.util.api.ResponseCode;
import com.diareat.diareat.util.exception.UserException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ public ApiResponse<Void> updateFood(UpdateFoodDto updateFoodDto){
//음식 삭제
@Operation(summary = "[음식] 음식 정보 삭제",description = "음식에 대한 정보를 삭제합니다.")
@DeleteMapping("/{foodId}/delete")
public ApiResponse<Void> deleteFood(@PathVariable Long foodId){
foodService.deleteFood(foodId);
public ApiResponse<Void> deleteFood(@PathVariable Long foodId, @RequestHeader Long userId){
Copy link
Contributor

Choose a reason for hiding this comment

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

Redis는 key value쌍으로 캐싱을 하기 때문에 userId에 대한 정보가 받아와야 하죠~

foodService.deleteFood(foodId, userId);
return ApiResponse.success(null, ResponseCode.FOOD_DELETE_SUCCESS.getMessage());
}

Expand Down Expand Up @@ -79,8 +79,8 @@ public ApiResponse<Void> updateFavoriteFood(UpdateFavoriteFoodDto updateFavorite
//즐겨찾기 음식 해제
@Operation(summary = "[즐겨찾기] 즐겨찾기 해제",description = "유저의 즐겨찾기에 등록된 음식을 해제합니다.")
@DeleteMapping("/favorite/{favoriteFoodId}")
public ApiResponse<Void> deleteFavoriteFood(@PathVariable Long favoriteFoodId){
foodService.deleteFavoriteFood(favoriteFoodId);
public ApiResponse<Void> deleteFavoriteFood(@PathVariable Long favoriteFoodId, @RequestHeader Long userId){
foodService.deleteFavoriteFood(favoriteFoodId, userId);
return ApiResponse.success(null, ResponseCode.FOOD_FAVORITE_DELETE_SUCCESS.getMessage());
}

Expand Down
7 changes: 5 additions & 2 deletions src/main/java/com/diareat/diareat/food/dto/CreateFoodDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDate;

@Getter
@NoArgsConstructor
@AllArgsConstructor
Expand All @@ -13,8 +15,9 @@ public class CreateFoodDto {
private Long userId;
private String name;
private BaseNutrition baseNutrition;
private LocalDate date;

public static CreateFoodDto of(Long userId, String name, BaseNutrition baseNutrition) {
return new CreateFoodDto(userId, name, baseNutrition);
public static CreateFoodDto of(Long userId, String name, BaseNutrition baseNutrition, LocalDate date) {
return new CreateFoodDto(userId, name, baseNutrition, date);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
public class UpdateFavoriteFoodDto {

private Long favoriteFoodId;
private Long userId;
private String name;
private BaseNutrition baseNutrition;

public static UpdateFavoriteFoodDto of(Long favoriteFoodId, String name, BaseNutrition baseNutrition) {
return new UpdateFavoriteFoodDto(favoriteFoodId, name, baseNutrition);
public static UpdateFavoriteFoodDto of(Long favoriteFoodId, Long userId, String name, BaseNutrition baseNutrition) {
return new UpdateFavoriteFoodDto(favoriteFoodId, userId, name, baseNutrition);
}
}
5 changes: 3 additions & 2 deletions src/main/java/com/diareat/diareat/food/dto/UpdateFoodDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
public class UpdateFoodDto {

private Long foodId;
private Long userId;
private String name;
private BaseNutrition baseNutrition;

public static UpdateFoodDto of(Long foodId, String name, BaseNutrition baseNutrition) {
return new UpdateFoodDto(foodId, name, baseNutrition);
public static UpdateFoodDto of(Long foodId, Long userId, String name, BaseNutrition baseNutrition) {
return new UpdateFoodDto(foodId, userId, name, baseNutrition);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@

public interface FavoriteFoodRepository extends JpaRepository<FavoriteFood, Long> {
List<FavoriteFood> findAllByUserId(Long userId);
boolean existsByIdAndUserId(Long id, Long userId); // 유저가 즐겨찾기에 추가한 음식인지 확인
boolean existsByFoodId(Long foodId); // 이미 즐겨찾기에 추가된 음식인지 확인하기 위함
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.List;

public interface FoodRepository extends JpaRepository<Food, Long> {
boolean existsByIdAndUserId(Long id, Long userId); // 유저가 먹은 음식인지 확인
List<Food> findAllByUserIdAndDate(Long userId, LocalDate date); //유저가 특정 날짜에 먹은 음식 반환
List<Food> findAllByUserIdAndDateBetween(Long userId, LocalDate startDate, LocalDate endDate); // 유저가 특정 기간 내에 먹은 음식 반환
}
39 changes: 28 additions & 11 deletions src/main/java/com/diareat/diareat/food/service/FoodService.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@
import com.diareat.diareat.food.repository.FoodRepository;
import com.diareat.diareat.user.domain.BaseNutrition;
import com.diareat.diareat.user.domain.User;
import com.diareat.diareat.user.dto.ResponseRankUserDto;
import com.diareat.diareat.user.dto.response.ResponseRankUserDto;
import com.diareat.diareat.user.repository.FollowRepository;
import com.diareat.diareat.user.repository.UserRepository;
import com.diareat.diareat.util.api.ResponseCode;
import com.diareat.diareat.util.exception.FavoriteException;
import com.diareat.diareat.util.exception.FoodException;
import com.diareat.diareat.util.exception.UserException;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.cache.annotation.Cacheable;

import java.time.DayOfWeek;
import java.time.LocalDate;
Expand All @@ -33,14 +35,16 @@ public class FoodService {
private final UserRepository userRepository;

// 촬영 후, 음식 정보 저장
@CacheEvict(value = "ResponseFoodDto", key = "#createFoodDto.getUserId()+#createFoodDto.getDate()", cacheManager = "diareatCacheManager")
@Transactional
public Long saveFood(CreateFoodDto createFoodDto) {
User user = getUserById(createFoodDto.getUserId());
Food food = Food.createFood(createFoodDto.getName(), user, createFoodDto.getBaseNutrition());
return foodRepository.save(food).getId();
}

// 회원이 특정 날짜에 먹은 음식 반환
// 회원이 특정 날짜에 먹은 음식 조회
@Cacheable(value = "ResponseFoodDto", key = "#userId+#date", cacheManager = "diareatCacheManager")
@Transactional(readOnly = true)
public List<ResponseFoodDto> getFoodListByDate(Long userId, LocalDate date){
validateUser(userId);
Expand All @@ -49,22 +53,25 @@ public List<ResponseFoodDto> getFoodListByDate(Long userId, LocalDate date){
.map(food -> ResponseFoodDto.of(food.getId(), food.getUser().getId(), food.getName(), food.getDate(), food.getTime(), food.getBaseNutrition(), food.isFavorite())).collect(Collectors.toList());
}


// 음식 정보 수정
@CacheEvict(value = "ResponseFoodDto", key = "#updateFoodDto.getUserId()", cacheManager = "diareatCacheManager")
@Transactional
public void updateFood(UpdateFoodDto updateFoodDto) {
Food food = getFoodById(updateFoodDto.getFoodId());
food.updateFood(updateFoodDto.getName(), updateFoodDto.getBaseNutrition());
foodRepository.save(food);
}

// 음식 삭제
@CacheEvict(value = "ResponseFoodDto", key = "#userId", cacheManager = "diareatCacheManager")
@Transactional
public void deleteFood(Long foodId) {
validateFood(foodId);
public void deleteFood(Long foodId, Long userId) {
validateFood(foodId, userId);
foodRepository.deleteById(foodId);
}

// 즐겨찾기에 음식 저장
@CacheEvict(value = "ResponseFavoriteFoodDto", key = "#createFavoriteFoodDto.getUserId()", cacheManager = "diareatCacheManager")
@Transactional
public Long saveFavoriteFood(CreateFavoriteFoodDto createFavoriteFoodDto) {
User user = getUserById(createFavoriteFoodDto.getUserId());
Expand All @@ -75,6 +82,7 @@ public Long saveFavoriteFood(CreateFavoriteFoodDto createFavoriteFoodDto) {
}

//즐겨찾기 음식 리스트 반환
@Cacheable(value = "ResponseFavoriteFoodDto", key = "#userId", cacheManager = "diareatCacheManager")
@Transactional(readOnly = true)
public List<ResponseFavoriteFoodDto> getFavoriteFoodList(Long userId){
validateUser(userId);
Expand All @@ -85,19 +93,23 @@ public List<ResponseFavoriteFoodDto> getFavoriteFoodList(Long userId){
}

// 즐겨찾기 음식 수정
@CacheEvict(value = "ResponseFavoriteFoodDto", key = "updateFavoriteFoodDto.getUserId()", cacheManager = "diareatCacheManager")
@Transactional
public void updateFavoriteFood(UpdateFavoriteFoodDto updateFavoriteFoodDto) {
FavoriteFood food = getFavoriteFoodById(updateFavoriteFoodDto.getFavoriteFoodId());
food.updateFavoriteFood(updateFavoriteFoodDto.getName(), updateFavoriteFoodDto.getBaseNutrition());
favoriteFoodRepository.save(food);
}

// 즐겨찾기 해제
@CacheEvict(value = "ResponseFavoriteFoodDto", key = "#userId", cacheManager = "diareatCacheManager")
@Transactional
public void deleteFavoriteFood(Long favoriteFoodId) {
validateFavoriteFood(favoriteFoodId);
public void deleteFavoriteFood(Long favoriteFoodId, Long userId) {
validateFavoriteFood(favoriteFoodId, userId);
favoriteFoodRepository.deleteById(favoriteFoodId);
}

@Cacheable(value = "ResponseNutritionSumByDateDto", key = "#userId+#date", cacheManager = "diareatCacheManager")
@Transactional(readOnly = true)
// 유저의 특정 날짜에 먹은 음식들의 영양성분별 총합 조회 (섭취영양소/기준영양소 및 비율까지 계산해서 반환, dto 구체적 협의 필요)
public ResponseNutritionSumByDateDto getNutritionSumByDate(Long userId, LocalDate date) {
Expand Down Expand Up @@ -178,6 +190,7 @@ public ResponseFoodRankDto getWorstFoodByWeek(Long userId) {
// 유저의 일기 분석 그래프 데이터 및 식습관 totalScore 조회


@Cacheable(value = "ResponseRankUserDto", key = "#userId", cacheManager = "diareatCacheManager")
@Transactional(readOnly = true)
// 유저의 식습관 점수를 기반으로 한 주간 랭킹 조회
public List<ResponseRankUserDto> getUserRankByWeek(Long userId) {
Expand Down Expand Up @@ -268,14 +281,18 @@ private void validateUser(Long userId) {
throw new UserException(ResponseCode.USER_NOT_FOUND);
}

private void validateFood(Long foodId) {
if (!foodRepository.existsById(foodId))
private void validateFood(Long foodId, Long userId) {
if(!foodRepository.existsById(foodId))
throw new FoodException(ResponseCode.FOOD_NOT_FOUND);
if (!foodRepository.existsByIdAndUserId(foodId, userId)) // 음식의 주인이 유저인지 아닌지 판정
throw new FoodException(ResponseCode.NOT_FOOD_OWNER);
}

private void validateFavoriteFood(Long favoriteFoodId) {
if (!favoriteFoodRepository.existsById(favoriteFoodId))
private void validateFavoriteFood(Long favoriteFoodId, Long userId) {
if(!favoriteFoodRepository.existsById(favoriteFoodId))
throw new FavoriteException(ResponseCode.FAVORITE_NOT_FOUND);
if(!favoriteFoodRepository.existsByIdAndUserId(favoriteFoodId, userId)) // 즐겨찾는 음식의 주인이 유저인지 아닌지 판정
throw new FavoriteException(ResponseCode.NOT_FOOD_OWNER);
}

// 1주일동안 먹은 음식들의 영양성분 총합을 요일을 Key로 한 Map을 통해 반환
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package com.diareat.diareat.user.controller;

import com.diareat.diareat.user.dto.*;
import com.diareat.diareat.user.dto.request.SearchUserDto;
import com.diareat.diareat.user.dto.request.UpdateUserDto;
import com.diareat.diareat.user.dto.request.UpdateUserNutritionDto;
import com.diareat.diareat.user.dto.response.ResponseSearchUserDto;
import com.diareat.diareat.user.dto.response.ResponseSimpleUserDto;
import com.diareat.diareat.user.dto.response.ResponseUserDto;
import com.diareat.diareat.user.dto.response.ResponseUserNutritionDto;
import com.diareat.diareat.user.service.UserService;
import com.diareat.diareat.util.api.ApiResponse;
import com.diareat.diareat.util.api.ResponseCode;
Expand All @@ -9,6 +15,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;

@Api(tags = "1. User")
Expand Down Expand Up @@ -36,7 +43,7 @@ public ApiResponse<ResponseUserDto> getUserInfo(@PathVariable Long userId) {
// 회원정보 수정
@Operation(summary = "[프로필] 회원 정보 수정", description = "회원 정보를 수정합니다.")
@PutMapping("/update")
public ApiResponse<Void> updateUserInfo(UpdateUserDto updateUserDto) {
public ApiResponse<Void> updateUserInfo(@Valid @RequestBody UpdateUserDto updateUserDto) {
userService.updateUserInfo(updateUserDto);
return ApiResponse.success(null, ResponseCode.USER_UPDATE_SUCCESS.getMessage());
}
Expand All @@ -51,33 +58,31 @@ public ApiResponse<ResponseUserNutritionDto> getUserNutrition(@PathVariable Long
// 회원 기준섭취량 직접 수정
@Operation(summary = "[프로필] 회원 기준섭취량 직접 수정", description = "회원 기준섭취량을 직접 수정합니다.")
@PutMapping("{userId}/nutrition")
public ApiResponse<Void> updateUserNutrition(UpdateUserNutritionDto updateUserNutritionDto) {
public ApiResponse<Void> updateUserNutrition(@Valid @RequestBody UpdateUserNutritionDto updateUserNutritionDto) {
userService.updateBaseNutrition(updateUserNutritionDto);
return ApiResponse.success(null, ResponseCode.USER_UPDATE_SUCCESS.getMessage());
}

// 회원의 친구 검색 결과 조회
@Operation(summary = "[주간 랭킹] 회원의 친구 검색 결과 조회", description = "회원의 친구 검색 결과를 조회합니다.")
@GetMapping("{userId}/search/{name}")
public ApiResponse<List<ResponseSearchUserDto>> searchUser(@PathVariable Long userId, @RequestParam String name) {
return ApiResponse.success(userService.searchUser(userId, name), ResponseCode.USER_SEARCH_SUCCESS.getMessage());
@GetMapping("/search")
public ApiResponse<List<ResponseSearchUserDto>> searchUser(@Valid @RequestBody SearchUserDto searchUserDto) {
return ApiResponse.success(userService.searchUser(searchUserDto.getUserId(), searchUserDto.getInputName()), ResponseCode.USER_SEARCH_SUCCESS.getMessage());
}

// 실제 팔로잉 유저들의 점수 계산하여 랭킹 형태로 반환하는 API: FoodService에서 계산할지 UserService에서 FoodRepository를 콜해서 처리할지 협의 필요

// 회원이 특정 회원 팔로우
@Operation(summary = "[주간 랭킹] 회원이 특정 회원 팔로우", description = "회원이 특정 회원을 팔로우합니다.")
@PostMapping("{userId}/follow/{followId}")
public ApiResponse<Void> followUser(@PathVariable Long userId, @PathVariable Long followId) {
userService.followUser(userId, followId);
return ApiResponse.success(null, ResponseCode.USER_UPDATE_SUCCESS.getMessage());
return ApiResponse.success(null, ResponseCode.USER_FOLLOW_SUCCESS.getMessage());
}

// 회원이 특정 회원 팔로우 취소
@Operation(summary = "[주간 랭킹] 회원이 특정 회원 팔로우 취소", description = "회원이 특정 회원을 팔로우 취소합니다.")
@DeleteMapping("{userId}/follow/{followId}")
public ApiResponse<Void> unfollowUser(@PathVariable Long userId, @PathVariable Long followId) {
userService.unfollowUser(userId, followId);
return ApiResponse.success(null, ResponseCode.USER_UPDATE_SUCCESS.getMessage());
return ApiResponse.success(null, ResponseCode.USER_UNFOLLOW_SUCCESS.getMessage());
}
}
14 changes: 0 additions & 14 deletions src/main/java/com/diareat/diareat/user/dto/FollowUserDto.java

This file was deleted.

18 changes: 0 additions & 18 deletions src/main/java/com/diareat/diareat/user/dto/JoinUserDto.java

This file was deleted.

Loading