Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ public ResultResponse<?> uploadPhoto(Long dietId, MultipartFile image) {
Diet diet = getDietOrThrow(dietId);
String imageUrl = s3UploadService.saveFile(image, imageUploadPath);
diet.updateImage(imageUrl);
return ResultResponse.of(ResultCode.CHILD_PHOTO_UPLOAD_SUCCESS, null);
return ResultResponse.of(ResultCode.DIET_ADD_IMAGE_SUCCESS, null);
}

@Override
Expand Down Expand Up @@ -235,7 +235,7 @@ public ResultResponse<?> updateDietTime(Long dietId, String newTime) {
authenticatedProvider.getAuthenticatedParent();
Diet diet = getDietOrThrow(dietId);
diet.updateTime(parseTimeOrThrow(newTime));
return ResultResponse.of(ResultCode.DIET_TIME_UPDATE_SUCCESS, null);
return ResultResponse.of(ResultCode.DIET_EDIT_SUCCESS, null);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public ResultResponse<?> getDietSetDetail(Long dietSetId) {
).toList();

DietSetDetailResponseDto response = new DietSetDetailResponseDto(set.getSetName(), foodDetails);
return ResultResponse.of(ResultCode.DIET_SET_DETAIL_SUCCESS, response);
return ResultResponse.of(ResultCode.DIET_SET_RETRIEVAL_SUCCESS, response);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

@Getter
@Entity
@Table(name = "goal")
@Table(name = "cert_goal")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Certification {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,14 @@ public ResponseEntity<?> loginChild(@RequestBody AuthChildRequestDto request, Ht
ResultResponse<?> resultResponse = authChildService.login(request, response);

return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
}

@Operation(summary = "아이 로그아웃")
@PostMapping("/logout")
public ResponseEntity<?> logoutChild(HttpServletResponse response) {
ResultResponse<String> resultResponse = authChildService.logout(response);

return ResponseEntity.status(HttpStatus.OK).body(resultResponse);
}

@Operation(summary = "아이 인증코드로 ID 찾기")
Expand All @@ -51,7 +58,6 @@ public ResponseEntity<?> findChildIdByCode(@RequestParam String code) {
ResultResponse<?> resultResponse = authChildService.findChildID(code);

return ResponseEntity.status(HttpStatus.OK).body(resultResponse);

}

@Operation(summary = "아이 비밀번호 재설정")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@
import com.project.growfit.domain.User.dto.response.ParentLoginResponseDto;
import com.project.growfit.domain.User.service.OauthService;
import com.project.growfit.global.exception.BusinessException;
import com.project.growfit.global.response.ResultCode;
import com.project.growfit.global.response.ResultResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/oauth")
Expand All @@ -23,9 +21,9 @@ public class OAuthController {

private final OauthService oauthService;

@Operation(summary = "카카오 소셜 로그인 콜백 컨트롤러 입니다.")
@Operation(summary = "카카오 소셜 로그인 API")
@GetMapping("/callback/kakao")
public void getKaKaoAuthorizeCode(@RequestParam(value = "code", required = false) String code,
public void kakaoLogin(@RequestParam(value = "code", required = false) String code,
HttpServletResponse response) {
if (code == null) {
try {
Expand Down Expand Up @@ -57,4 +55,12 @@ public void getKaKaoAuthorizeCode(@RequestParam(value = "code", required = false
}
}
}

@Operation(summary = "카카오 소셜 로그아웃 API")
@PostMapping("/logout")
public ResultResponse<String> kakaoLogout(@RequestParam(value = "code", required = false) String code,
HttpServletResponse response) {
return oauthService.kakaoLogout(code, response);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ public ResultResponse<?> generateToken(@RequestBody Map<String, String> request,

log.info("로그인 성공: email={}, accessToken 저장 완료", email);
ParentLoginResponseDto dto = new ParentLoginResponseDto(email, true);
return new ResultResponse<>(ResultCode.PARENT_LOGIN_SUCCESS, dto);
return new ResultResponse<>(ResultCode.LOGIN_SUCCESS, dto);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public interface AuthChildService {
ResultResponse<?> findByCode(String code);
ResultResponse<?> registerChildCredentials(Long child_id, AuthChildRequestDto request);
ResultResponse<?> login(AuthChildRequestDto request, HttpServletResponse response);
ResultResponse<String> logout(HttpServletResponse response);
ResultResponse<?> findChildID(String code);
ResultResponse<?> findChildPassword(FindChildPasswordRequestDto request);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ public interface OauthService {
String getKakaoAccessToken(String code);
HashMap<String, Object> getUserKakaoInfo(String access_token);
ResultResponse<?> kakaoLogin(String access_token, HttpServletResponse response);
ResultResponse<String> kakaoLogout(String access_token, HttpServletResponse response);
ParentResponse findByUserKakaoIdentifier(String kakaoIdentifier);
Long signUp(ParentOAuthRequestDto requestDto);


}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.project.growfit.domain.User.service.AuthChildService;
import com.project.growfit.global.auth.cookie.CookieService;
import com.project.growfit.global.auth.jwt.JwtProvider;
import com.project.growfit.global.auth.service.AuthenticatedUserProvider;
import com.project.growfit.global.auth.service.CustomAuthenticationProvider;
import com.project.growfit.global.exception.BusinessException;
import com.project.growfit.global.exception.ErrorCode;
Expand Down Expand Up @@ -37,14 +38,15 @@ public class AuthChildServiceImpl implements AuthChildService {
private final JwtProvider jwtProvider;
private final TokenRedisRepository tokenRedisRepository;
private final CustomAuthenticationProvider authenticationProvider;
private final AuthenticatedUserProvider authenticatedUser;

public ResultResponse<?> findByCode(String code) {
log.info("[findByCode] 코드로 아이 정보 조회 요청: {}", code);
Child child = getChild(code);
Long childPid = child.getId();

log.info("[findByCode] 아이 정보 PID 조회 성공: {}", childPid);
return new ResultResponse<>(ResultCode.CHILD_INFO_RETRIEVAL_SUCCESS, new ChildIdResponse(childPid));
return new ResultResponse<>(ResultCode.INFO_SUCCESS, new ChildIdResponse(childPid));
}

@Override
Expand All @@ -62,7 +64,7 @@ public ResultResponse<?> registerChildCredentials(Long child_id, AuthChildReques
}

log.info("[registerChildCredentials] 아이 계정 정보 등록 완료: child_id={}", child_id);
return new ResultResponse<>(ResultCode.PARENT_SIGNUP_SUCCESS, null);
return new ResultResponse<>(ResultCode.INFO_REGISTRATION_SUCCESS, null);
}


Expand Down Expand Up @@ -95,7 +97,20 @@ public ResultResponse<?> login(AuthChildRequestDto request, HttpServletResponse
cookieService.saveAccessTokenToCookie(response, newAccessToken);
log.debug("[login] AccessToken을 쿠키에 저장 완료: child_login_id={}", request.childId());

return new ResultResponse<>(ResultCode.CHILD_LOGIN_SUCCESS, null);
return new ResultResponse<>(ResultCode.LOGIN_SUCCESS, null);
}

public ResultResponse<String> logout(HttpServletResponse response) {
String loginId = authenticatedUser.getAuthenticatedChild().getLoginId();
log.info("[logout] 아이 로그아웃 요청: loginId={}", loginId);

tokenRedisRepository.deleteById(loginId);
log.debug("[logout] Redis에서 리프레시 토큰 삭제 완료: loginId={}", loginId);

cookieService.clearCookie(response, "accessToken");
log.debug("[logout] accessToken 쿠키 만료 처리 완료: loginId={}", loginId);

return new ResultResponse<>(ResultCode.LOGOUT_SUCCESS, null);
}

public ResultResponse<?> findChildID(String code) {
Expand All @@ -104,7 +119,7 @@ public ResultResponse<?> findChildID(String code) {
ChildInfoResponseDto dto = ChildInfoResponseDto.toDto(child);

log.info("[findChildID] 아이 ID 찾기 성공: {}", dto);
return new ResultResponse<>(ResultCode.CHILD_INFO_RETRIEVAL_SUCCESS, dto);
return new ResultResponse<>(ResultCode.INFO_SUCCESS, dto);
}

@Override
Expand All @@ -121,7 +136,7 @@ public ResultResponse<?> findChildPassword(FindChildPasswordRequestDto request)
childRepository.save(child);

log.info("[findChildPassword] 비밀번호 변경 완료: user_id={}", request.user_id());
return new ResultResponse<>(ResultCode.PARENT_SIGNUP_SUCCESS, null);
return new ResultResponse<>(ResultCode.INFO_REGISTRATION_SUCCESS, null);
}

private Child getChild(Long child_id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public ResultResponse<?> registerChild(CustomUserDetails user, AuthParentRequest
ChildInfoResponseDto dto = ChildInfoResponseDto.toDto(child);

log.info("[registerChild] 자녀 등록 완료: child_id={}, parent_id={}", child.getId(), parent.getId());
return new ResultResponse<>(ResultCode.PARENT_SIGNUP_SUCCESS, dto);
return new ResultResponse<>(ResultCode.SIGNUP_SUCCESS, dto);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.project.growfit.domain.User.service.OauthService;
import com.project.growfit.global.auth.cookie.CookieService;
import com.project.growfit.global.auth.jwt.JwtProvider;
import com.project.growfit.global.auth.service.AuthenticatedUserProvider;
import com.project.growfit.global.exception.BusinessException;
import com.project.growfit.global.exception.ErrorCode;
import com.project.growfit.global.redis.entity.TokenRedis;
Expand All @@ -26,6 +27,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
Expand All @@ -39,6 +41,7 @@ public class OauthServiceImpl implements OauthService {
private final JwtProvider jwtProvider;
private final ParentRepository parentRepository;
private final CookieService cookieService;
private final AuthenticatedUserProvider authenticatedUser;
private final RestTemplate restTemplate = new RestTemplate();
private final TokenRedisRepository tokenRedisRepository;

Expand All @@ -52,6 +55,8 @@ public class OauthServiceImpl implements OauthService {
private String kakaoClientSecret;
@Value("${spring.security.oauth2.client.registration.kakao.redirect-uri}")
private String kakaoRedirectUri;
@Value("${custom.oauth2.kakao.logout-uri}")
private String kakaoLogoutUri;


@Override
Expand Down Expand Up @@ -140,7 +145,26 @@ public ResultResponse<?> kakaoLogin(String accessToken, HttpServletResponse resp

log.info("[kakaoLogin] 카카오 로그인 성공: email={}, accessToken 저장 완료", parentResponse.email());
ParentLoginResponseDto dto = new ParentLoginResponseDto(parentResponse.email(), isNewUser);
return new ResultResponse<>(ResultCode.PARENT_LOGIN_SUCCESS, dto);
return new ResultResponse<>(ResultCode.LOGIN_SUCCESS, dto);
}

@Override
public ResultResponse<String> kakaoLogout(String access_token, HttpServletResponse response) {
Parent user = authenticatedUser.getAuthenticatedParent();
tokenRedisRepository.deleteById(user.getEmail());

HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(access_token);
HttpEntity<Void> request = new HttpEntity<>(headers);

cookieService.clearCookie(response, "accessToken");

try {
restTemplate.postForEntity(kakaoLogoutUri, request, String.class);
} catch (HttpClientErrorException e) {
System.err.println("카카오 로그아웃 실패: " + e.getMessage());
}
return ResultResponse.of(ResultCode.LOGOUT_SUCCESS, "");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.project.growfit.global.auth.cookie;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -26,6 +27,14 @@ public void saveEmailToCookie(HttpServletResponse response, String email) {
log.info("[saveEmailToCookie] 이메일이 쿠키에 저장되었습니다.");
}

public void clearCookie(HttpServletResponse response, String name) {
Cookie cookie = new Cookie(name, null);
cookie.setMaxAge(0);
cookie.setPath("/");
cookie.setHttpOnly(cookieProperties.isHttpOnly());
response.addCookie(cookie);
}

private ResponseCookie createCookie(String name, String value) {
return ResponseCookie.from(name, value)
.httpOnly(cookieProperties.isHttpOnly())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,16 @@ public Child getAuthenticatedChild() {
};
}

public CustomUserDetails getCurrentDetails() {
return (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}

private CustomUserDetails getCurrentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth == null || !auth.isAuthenticated()) {
throw new BusinessException(ErrorCode.FORBIDDEN_ACCESS);
}
Object principal = auth.getPrincipal();

if (!(principal instanceof CustomUserDetails)) {
throw new BusinessException(ErrorCode.FORBIDDEN_ACCESS);
}
return (CustomUserDetails) auth.getPrincipal();
}

Expand Down
59 changes: 34 additions & 25 deletions src/main/java/com/project/growfit/global/response/ResultCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,51 @@
public enum ResultCode {
RESPONSE_TEST(HttpStatus.OK, "응답 테스트 성공"),

PARENT_NICKNAME_SET_SUCCESS(HttpStatus.OK, "부모 닉네임이 성공적으로 설정되었습니다."),
PARENT_SIGNUP_SUCCESS(HttpStatus.OK, "부모 회원가입이 완료되었습니다."),
CHILD_INFO_RETRIEVAL_SUCCESS(HttpStatus.OK, "자녀 정보가 성공적으로 조회되었습니다."),
QR_GENERATION_SUCCESS(HttpStatus.OK, "아이등록 QR코드를 성공적으로 생성하였습니다."),
INFO_REGISTRATION_SUCCESS(HttpStatus.OK, "사용자 정보가 성공적으로 등록되었습니다."),
SIGNUP_SUCCESS(HttpStatus.OK, "회원가입이 완료되었습니다."),
QR_GENERATION_SUCCESS(HttpStatus.OK, "성공적으로 QR코드를 생성하였습니다."),
ID_AVAILABLE(HttpStatus.OK, "사용 가능한 아이디입니다."),

CHILD_LOGIN_SUCCESS(HttpStatus.OK, "아이 계정 로그인 성공."),
PARENT_LOGIN_SUCCESS(HttpStatus.OK, "부모 계정 로그인 성공."),

LOGIN_SUCCESS(HttpStatus.OK, "성공적으로 로그인하였습니다."),
LOGOUT_SUCCESS(HttpStatus.OK, "성공적으로 로그아웃하였습니다."),

INFO_SUCCESS(HttpStatus.OK, "성공적으로 정보가 조회되었습니다."),

//Diet
STICKER_DELETE_SUCCESS(HttpStatus.OK, "스티커 삭제에 성공했습니다."),
CHILD_PHOTO_DELETE_SUCCESS(HttpStatus.OK, "아이 식단 사진 삭제에 성공했습니다."),
DIET_SET_LIST_SUCCESS(HttpStatus.OK, "식단 세트 목록 조회에 성공하였습니다."),
DIET_SET_SAVE_SUCCESS(HttpStatus.CREATED, "식단 세트 저장에 성공하였습니다."),
DIET_SET_DETAIL_SUCCESS(HttpStatus.OK, "식단 세트 상세 조회에 성공하였습니다."),
DIET_OVERRIDE_SUCCESS(HttpStatus.OK, "식단 불이행 정보 입력에 성공했습니다."),
DIET_TIME_UPDATE_SUCCESS(HttpStatus.OK, "식단 시간 수정에 성공하였습니다."),
CHILD_PHOTO_UPLOAD_SUCCESS(HttpStatus.OK, "식단 이미지 업로드를 성공했습니다."),
CHILD_STATE_UPLOAD_SUCCESS(HttpStatus.OK, "식단 이행 상태 업데이트를 성공했습니다."),
STICKER_MARK_SUCCESS(HttpStatus.OK, "스티커가 성공적으로 등록되었습니다."),
STICKER_UPDATE_SUCCESS(HttpStatus.OK, "스티커가 수정되었습니다."),
STICKER_DELETE_SUCCESS(HttpStatus.OK, "스티커 삭제를 성공하였습니다."),

DIET_RETRIEVAL_SUCCESS(HttpStatus.OK, "식단을 성공적으로 조회했습니다."),
DIET_EDIT_SUCCESS(HttpStatus.OK, "식단 정보가 성공적으로 수정되었습니다."),
DIET_ADD_SUCCESS(HttpStatus.OK, "식단을 성공적으로 추가하였습니다."),
DIET_DELETE_SUCCESS(HttpStatus.NO_CONTENT, "식단이 성공적으로 삭제되었습니다."),

DAILY_DIET_RETRIEVAL_SUCCESS(HttpStatus.OK, "일일 식단 조회에 성공하였습니다."),
DIET_DETAIL_RETRIEVAL_SUCCESS(HttpStatus.OK, "음식 상세 정보를 성공적으로 조회하였습니다."),

DIET_SEARCH_SUCCESS(HttpStatus.OK, "식단 음식 검색에 성공하였습니다."),
DIET_SEARCH_RESULT_EMPTY(HttpStatus.OK, "검색 결과가 없습니다."),
DIET_DETAIL_RETRIEVAL_SUCCESS(HttpStatus.OK, "음식 상세 정보를 성공적으로 조회하였습니다."),
DIET_ADD_SUCCESS(HttpStatus.OK, "식단이 성공적으로 추가되었습니다."),

DIET_ADD_IMAGE_SUCCESS(HttpStatus.OK, "식단 사진이 성공적으로 업로드되었습니다."),
DAILY_DIET_RETRIEVAL_SUCCESS(HttpStatus.OK, "일일 식단 조회에 성공하였습니다."),
DIET_FOOD_DELETE_SUCCESS(HttpStatus.OK, "식단에서 음식이 성공적으로 삭제되었습니다."),
DIET_DELETE_SUCCESS(HttpStatus.NO_CONTENT, "식단이 성공적으로 삭제되었습니다."),
DIET_EDIT_SUCCESS(HttpStatus.OK, "식단 정보가 성공적으로 수정되었습니다."),
STICKER_MARK_SUCCESS(HttpStatus.OK, "스티커가 성공적으로 등록되었습니다."),
CALENDAR_OVERVIEW_SUCCESS(HttpStatus.OK, "식단 캘린더 조회에 성공했습니다."),
DIET_COPY_SUCCESS(HttpStatus.OK, "식단 복사에 성공했습니다."),
CHILD_PHOTO_DELETE_SUCCESS(HttpStatus.OK, "식단 사진을 삭제하였습니다."),
//DIET_TIME_UPDATE_SUCCESS(HttpStatus.OK, "식단 시간 수정에 성공하였습니다."),

DIET_SET_LIST_SUCCESS(HttpStatus.OK, "식단 세트 목록 조회에 성공하였습니다."),
DIET_SET_SAVE_SUCCESS(HttpStatus.CREATED, "식단 세트 저장에 성공하였습니다."),
//DIET_SET_DETAIL_SUCCESS(HttpStatus.OK, "식단 세트 상세 조회에 성공하였습니다."),
DIET_SET_RETRIEVAL_SUCCESS(HttpStatus.OK, "식단 세트 조회에 성공했습니다."),
DIET_SET_DELETE_SUCCESS(HttpStatus.OK, "식단 세트 삭제에 성공했습니다."),
DIET_SET_EDIT_SUCCESS(HttpStatus.OK, "식단 세트 수정에 성공했습니다." ),
STICKER_UPDATE_SUCCESS(HttpStatus.OK, "스티커가 수정되었습니다."),

DIET_OVERRIDE_SUCCESS(HttpStatus.OK, "식단 불이행 정보 입력에 성공했습니다."),
CHILD_STATE_UPLOAD_SUCCESS(HttpStatus.OK, "식단 이행 상태 업데이트를 성공했습니다."),
DIET_FOOD_DELETE_SUCCESS(HttpStatus.OK, "식단에서 음식이 성공적으로 삭제되었습니다."),

CALENDAR_OVERVIEW_SUCCESS(HttpStatus.OK, "식단 캘린더 조회에 성공했습니다."),


// Community
CREATE_POST_SUCCESS(HttpStatus.OK, "글 등록 성공."),
GET_POST_SUCCESS(HttpStatus.OK, "글 조회 성공"),
Expand Down
Loading