Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
d71bfb7
:sparkles: feat: 마이페이지 Response DTO 추가
ojy0903 Feb 6, 2026
cfefe28
:sparkles: feat: 마이페이지 캐싱을 위한 RedisConfig 추가
ojy0903 Feb 6, 2026
f5b69d0
:sparkles: feat: 마이페이지 Entity -> Respone DTO 변환 Converter 메서드 추가
ojy0903 Feb 6, 2026
243fac4
:sparkles: feat: 마이페이지 Service 메서드 추가
ojy0903 Feb 6, 2026
b0b4a43
:sparkles: feat: 마이페이지 API 추가 & Swagger Docs 추가
ojy0903 Feb 6, 2026
d2cc693
Merge remote-tracking branch 'origin/develop' into feat/#31
ojy0903 Feb 9, 2026
f35a7f1
:sparkles: feat: SecurityConfig 마이페이지 접근 시 인증 요구 메서드 추가
ojy0903 Feb 9, 2026
e8f39ec
:sparkles: feat: 마이페이지 Provider enum 에 이메일 로그인 유형 추가
ojy0903 Feb 14, 2026
ae0d98a
:sparkles: feat: 소셜 로그인 Provider 반환 로직 추가
ojy0903 Feb 14, 2026
f9414ab
:sparkles: feat: 일반 이메일 로그인 Provider 반환 로직 추가
ojy0903 Feb 14, 2026
b15de47
:sparkles: feat: JWT 토큰에 Provider 정보 추가, JWT 필터에서 Provider 를 올바르게 반환하…
ojy0903 Feb 14, 2026
c039cc4
:sparkles: feat: 마이페이지 Response DTO 에 Provider 추가 (String 으로 출력)
ojy0903 Feb 14, 2026
e30d91b
:sparkles: feat: UserService 마이페이지 조회 캐시 로직 수정, Converter DTO 에 맞게 추가
ojy0903 Feb 14, 2026
3fcd035
:sparkles: feat: 마이페이지 API Controller 에 추가
ojy0903 Feb 14, 2026
b88478b
:sparkles: feat: 미사용 import 문 제거
ojy0903 Feb 14, 2026
41f9655
:sparkles: feat: 디버그용 출력 코드 제거
ojy0903 Feb 14, 2026
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,11 @@
package com.whereyouad.WhereYouAd.domains.user.application.dto.response;

public record MyPageResponse(
Long userId,
String email,
String name,
String profileImageUrl,
String phoneNumber,
boolean isEmailVerified
) {
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.whereyouad.WhereYouAd.domains.user.application.mapper;

import com.whereyouad.WhereYouAd.domains.user.application.dto.response.MyPageResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SignUpResponse;
import com.whereyouad.WhereYouAd.domains.user.domain.constant.UserStatus;
import com.whereyouad.WhereYouAd.domains.user.persistence.entity.User;
Expand Down Expand Up @@ -32,4 +33,14 @@ public static OAuth2UserInfo toOAuth2UserInfo(User user, OAuth2Response oAuth2Re
.provider(oAuth2Response.getProvider())
.build();
}

public static MyPageResponse toMyPageResponse(User user) {
return new MyPageResponse(user.getId(),
user.getEmail(),
user.getName(),
user.getProfileImageUrl(),
user.getPhoneNumber(),
user.isEmailVerified()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.whereyouad.WhereYouAd.domains.user.domain.service;

import com.whereyouad.WhereYouAd.domains.user.application.dto.response.MyPageResponse;
import com.whereyouad.WhereYouAd.domains.user.exception.handler.UserHandler;
import com.whereyouad.WhereYouAd.domains.user.exception.code.UserErrorCode;
import com.whereyouad.WhereYouAd.domains.user.domain.constant.UserStatus;
Expand All @@ -10,6 +11,7 @@
import com.whereyouad.WhereYouAd.domains.user.persistence.repository.UserRepository;
import com.whereyouad.WhereYouAd.global.utils.RedisUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -85,4 +87,19 @@ public void passwordReset(String email, String password) {

redisUtil.deleteData("VERIFIED:" + email);
}

/**
* 마이페이지 메서드 Redis 캐싱 적용
* 동작 방식: Redis에서 'user:profile::{userId}' 키를 먼저 조회,
* 데이터가 존재하면 메서드를 실행하지 않고(DB 조회 X) 캐시된 데이터를 즉시 반환
* 데이터가 없으면 DB에서 조회 후 반환, 결과값을 자동으로 Redis에 저장
*/
@Cacheable(value = "user:profile", key = "#userId", unless = "#result == null")
@Transactional(readOnly = true)
public MyPageResponse getMyPage(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new UserHandler(UserErrorCode.USER_NOT_FOUND));

return UserConverter.toMyPageResponse(user);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# UserService.java 파일 확인
find . -type f -name "UserService.java" | head -1 | xargs wc -l

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 163


🏁 Script executed:

# 사용자 프로필 업데이트/수정 흐름이 있는지 확인
rg -n "updateProfile|updatePhone|updateImage|updateUser|@CacheEvict" src/main/java --type java -i

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 362


🏁 Script executed:

# UserService.java 전체 내용 확인
find . -type f -name "UserService.java" | head -1 | xargs cat -n

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 5117


🏁 Script executed:

# CustomOAuth2UserService.java 파일 확인 및 updateProfile 주변 코드 보기
find . -type f -name "CustomOAuth2UserService.java" | xargs wc -l

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 178


🏁 Script executed:

# CustomOAuth2UserService.java 전체 내용 확인
find . -type f -name "CustomOAuth2UserService.java" | xargs cat -n

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 5357


🏁 Script executed:

# 프로필 업데이트 관련 컨트롤러 또는 엔드포인트 검색
rg -n "updateProfile|updateUser|updatePhone|updateImage" src/main/java --type java -B 3 -A 5

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 2341


🏁 Script executed:

# User 엔티티에서 프로필 관련 메서드 확인
find . -type f -name "User.java" -path "*/domains/user/*" | xargs cat -n

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 1988


소셜 로그인 시 프로필 변경 후 캐시 무효화 처리 필수입니다.

현재 CustomOAuth2UserServiceloadUser() 메서드에서 기존 소셜 계정으로 로그인 시 user.updateProfile()로 프로필을 변경하고 있는데(95라인), getMyPage()의 Redis 캐시가 무효화되지 않고 있습니다. 이로 인해 사용자가 소셜 로그인으로 프로필을 업데이트한 후 마이페이지를 조회하면 변경되기 전의 캐시된 정보를 받게 되는 데이터 불일치 문제가 발생합니다.

해결 방법

CustomOAuth2UserService에서 프로필 변경 후 다음과 같이 캐시를 무효화해주세요:

`@Override`
`@Transactional`
`@CacheEvict`(cacheNames = "user:profile", key = "#result.id")  // 로그인 후 해당 사용자 캐시 제거
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
    // ... 기존 코드 ...
    user.updateProfile(oAuth2Response.getName());  // 95라인
    // ... 나머지 코드 ...
}

또는 UserService에 프로필 변경 전담 메서드를 추가하고 거기서 캐시를 처리하는 방식도 좋습니다:

`@Transactional`
`@CacheEvict`(cacheNames = "user:profile", key = "#userId")
public void updateUserProfile(Long userId, String name) {
    User user = userRepository.findById(userId)
            .orElseThrow(() -> new UserHandler(UserErrorCode.USER_NOT_FOUND));
    user.updateProfile(name);
}
🤖 Prompt for AI Agents
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java`
around lines 97 - 103, The Redis cache for getMyPage ("user:profile") isn't
evicted after profile changes done in CustomOAuth2UserService.loadUser (where
User.updateProfile(...) is called), causing stale data; fix by evicting the
cache for that user after updating the profile—either annotate loadUser with
`@CacheEvict`(cacheNames="user:profile", key="#result.id") (or otherwise call
cacheEvict for the user's id) immediately after User.updateProfile, or move
profile mutation into a new UserService.updateUserProfile(Long userId, String
name) method annotated with `@CacheEvict`(cacheNames="user:profile",
key="#userId") and call that from CustomOAuth2UserService.loadUser.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "UserService.java" | grep -E "src/main/java"

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 159


🏁 Script executed:

fd "UserService.java" src/main/java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 257


🏁 Script executed:

fd "UserService" src/main/java --type f

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 257


🏁 Script executed:

git ls-files src/main/java | grep -i userservice

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 257


🏁 Script executed:

cat -n src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java | head -120

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 5117


🏁 Script executed:

wc -l src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 161


🏁 Script executed:

fd ".*Controller.*" src/main/java --type f | grep -i user

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 434


🏁 Script executed:

git ls-files src/main/java | grep -i controller | grep -i user

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 434


🏁 Script executed:

rg "getMyPage" src/main/java --type java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 797


🏁 Script executed:

cat -n src/main/java/com/whereyouad/WhereYouAd/domains/user/presentation/UserController.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 5127


🏁 Script executed:

wc -l src/main/java/com/whereyouad/WhereYouAd/domains/user/presentation/UserController.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 161


🏁 Script executed:

rg "@AuthenticationPrincipal|@PreAuthorize|@Secured|@RolesAllowed" src/main/java/com/whereyouad/WhereYouAd/domains/user/presentation/UserController.java -A 2

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 267


🏁 Script executed:

fd "SecurityConfig|SecurityConfiguration" src/main/java --type f

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 148


🏁 Script executed:

git ls-files src/main/java | grep -i "security" | grep -i "config"

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 148


🏁 Script executed:

cat -n src/main/java/com/whereyouad/WhereYouAd/global/security/SecurityConfig.java | head -150

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 5187


🏁 Script executed:

wc -l src/main/java/com/whereyouad/WhereYouAd/global/security/SecurityConfig.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 151


🏁 Script executed:

fd "JwtAuthenticationFilter" src/main/java --type f

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 161


🏁 Script executed:

rg "userId" src/main/java/com/whereyouad/WhereYouAd/global/security --type java -B 2 -A 2

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 55


🏁 Script executed:

git ls-files src/main/java | grep -i "principal\|userdetails\|authentication" | head -20

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 538


🏁 Script executed:

cat -n src/main/java/com/whereyouad/WhereYouAd/global/security/jwt/CustomUserDetails.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 2393


🏁 Script executed:

cat -n src/main/java/com/whereyouad/WhereYouAd/global/security/jwt/JwtAuthenticationFilter.java | head -80

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 4008


🏁 Script executed:

cat -n src/main/java/com/whereyouad/WhereYouAd/global/security/jwt/JwtAuthenticationFilter.java | tail -30

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 1401


🏁 Script executed:

rg "GlobalExceptionHandler|@ExceptionHandler|ControllerAdvice" src/main/java --type java -l

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 157


🏁 Script executed:

git ls-files src/main/java | grep -i "exceptionhandler\|exception"

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 1010


🏁 Script executed:

cat -n src/main/java/com/whereyouad/WhereYouAd/global/exception/GlobalExceptionHandler.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 5191


🏁 Script executed:

web_search
Spring `@AuthenticationPrincipal` null when unauthenticated Spring Security 6

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 168


🏁 Script executed:

web_search
Spring `@Cacheable` null key IllegalArgumentException Spring Boot 3

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 168


🌐 Web query:

Spring @AuthenticationPrincipal null when unauthenticated Spring Security 6

💡 Result:

In Spring Security 6, @AuthenticationPrincipal can be null for unauthenticated/anonymous requests because Spring MVC resolves it from HttpServletRequest#getPrincipal(), and for anonymous requests that servlet principal is null—even though Spring Security may still have an AnonymousAuthenticationToken in the SecurityContext. [1]

Also, the @AuthenticationPrincipal resolver returns null when Authentication or Authentication.getPrincipal() is null. [2]

What to do instead (if you need “anonymous vs authenticated”)

Use @CurrentSecurityContext to read from Spring Security’s SecurityContext instead of the servlet principal:

`@GetMapping`("/me")
public Object me(`@CurrentSecurityContext` SecurityContext context) {
  Authentication auth = context.getAuthentication(); // can be AnonymousAuthenticationToken
  return (auth == null) ? null : auth.getPrincipal();
}

@CurrentSecurityContext is specifically meant for resolving the SecurityContext (and supports SpEL via expression). [3]

If you only want the user when logged in

Keep @AuthenticationPrincipal and accept that it will be null when not logged in:

`@GetMapping`("/me")
public Object me(`@AuthenticationPrincipal` MyUserDetails user) {
  return user; // null when unauthenticated
}

Sources: Spring Security reference on anonymous + MVC principal resolution [1], Spring Security API behavior of principal resolution [2], @CurrentSecurityContext annotation API [3].


🌐 Web query:

Spring @Cacheable null key IllegalArgumentException Spring Boot 3 cache

💡 Result:

In Spring Boot 3.x (Spring Framework 6.1+), @Cacheable(key = "#paramName") can throw:

IllegalArgumentException: Null key returned for cache operation ... If you are using named parameters, ensure that the compiler uses the '-parameters' flag

because Spring no longer tries to infer Java parameter names from bytecode debug info; if your code isn’t compiled with -parameters, SpEL can’t resolve #username (etc.), so the key expression evaluates to null and caching fails. (github.com)

Fix options

  1. Compile with parameter-name retention (-parameters) (recommended)
  • Maven
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <parameters>true</parameters>
      </configuration>
    </plugin>
  • Gradle
    tasks.withType(JavaCompile).configureEach {
      options.compilerArgs.add("-parameters")
    }

This restores #paramName support. (github.com)

  1. Use index-based arguments in SpEL (works even without -parameters)
`@Cacheable`(cacheNames="user", key="#p0")   // or `#a0`

Spring explicitly documents #a0/#p0 as the fallback when parameter names aren’t available. (docs.enterprise.spring.io)

  1. If the actual argument value can be null, guard it
`@Cacheable`(cacheNames="user", key="#p0", condition="#p0 != null")

Note: unless is evaluated after invocation; it won’t prevent a failure caused by a null key computed up front. (docs.enterprise.spring.io)

Citations:


🏁 Script executed:

rg "getMyPage|updateMyPage|updateProfile" src/main/java --type java -B 2 -A 5

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 4733


🏁 Script executed:

rg "@CacheEvict|@CachePut|evict" src/main/java/com/whereyouad/WhereYouAd/domains/user --type java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 55


🏁 Script executed:

find . -name "pom.xml" -o -name "build.gradle" | head -5

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 87


🏁 Script executed:

cat pom.xml | grep -A 10 "maven-compiler-plugin"

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 112


🏁 Script executed:

cat build.gradle

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 1960


🏁 Script executed:

rg "CACHEABLE_CONFIG|cache:" src/main/java --type java -A 3

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 55


🏁 Script executed:

grep -r "parameters.*true\|compilerArgs.*parameters" . --include="*.gradle" --include="*.xml"

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 55


🏁 Script executed:

cat -n src/main/java/com/whereyouad/WhereYouAd/global/security/SecurityConfig.java | sed -n '48,52p'

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 484


🏁 Script executed:

python3 << 'EOF'
# Analyze Spring Security pattern matching for /api/users/my
patterns = [
    "/swagger-ui/**",
    "/v3/api-docs/**", 
    "/swagger-ui.html",
    "/api/users/**",
    "/api/auth/**"
]

endpoints = [
    "/api/users/my",
    "/api/users/signup",
    "/api/auth/login"
]

from fnmatch import fnmatch

print("Pattern Matching Analysis:")
print("=" * 60)
for endpoint in endpoints:
    matches = [p for p in patterns if fnmatch(endpoint, p)]
    if matches:
        print(f"✓ {endpoint} matches permitAll patterns: {matches}")
    else:
        print(f"✗ {endpoint} requires authentication")
EOF

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 351


/api/users/my 엔드포인트가 permitAll()로 노출되어 있어서 인증 없이 접근 가능합니다.

SecurityConfig의 .requestMatchers("/api/users/**").permitAll()로 인해 마이페이지 엔드포인트가 누구나 접근할 수 있게 열려 있습니다. 이 경우 @AuthenticationPrincipal(expression = "userId") Long userId는 null이 되고, null 키로 @Cacheable을 실행하면 예외가 발생할 수 있습니다.

해결책: 보호된 엔드포인트로 변경하세요.

.requestMatchers("/api/users/signup", "/api/users/email-send", "/api/users/email-verify", 
                 "/api/users/sms-send", "/api/users/sms-verify",
                 "/api/users/password-reset/**").permitAll()
.anyRequest().authenticated()

그리고 /api/users/my는 인증된 요청만 도달하도록 보호되어야 합니다. 현재 JWT 필터와 인증 메커니즘은 잘 설계되어 있으니, 설정만 바로잡으면 userId는 항상 유효한 값이 됩니다.

🤖 Prompt for AI Agents
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/UserService.java`
around lines 97 - 103, The API security currently allows all "/api/users/**" via
SecurityConfig.requestMatchers(...).permitAll(), which exposes the protected
"/api/users/my" endpoint and can cause a null `@AuthenticationPrincipal` (userId)
leading to cache/key errors in UserService.getMyPage (annotated with `@Cacheable`
and using userId); update SecurityConfig to only permit public endpoints
(signup, email-send, email-verify, sms-send, sms-verify, password-reset/**) in
requestMatchers and ensure .anyRequest().authenticated() is applied so that
"/api/users/my" requires authentication and userId is always present for
UserService.getMyPage.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.whereyouad.WhereYouAd.domains.user.application.dto.request.SmsRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.request.PwdResetRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.MyPageResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SmsResponse;
import com.whereyouad.WhereYouAd.domains.user.domain.service.EmailService;
import com.whereyouad.WhereYouAd.domains.user.domain.service.SmsService;
Expand All @@ -15,10 +16,8 @@
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
Expand Down Expand Up @@ -84,4 +83,13 @@ public ResponseEntity<DataResponse<String>> resetPassword(@RequestBody @Valid Pw
DataResponse.from("비밀번호 변경이 완료되었습니다.")
);
}

@GetMapping("/my")
public ResponseEntity<DataResponse<MyPageResponse>> getMyPage(@AuthenticationPrincipal(expression = "userId") Long userId) {
MyPageResponse response = userService.getMyPage(userId);

return ResponseEntity.ok(
DataResponse.from(response)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.whereyouad.WhereYouAd.domains.user.application.dto.request.PwdResetRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.request.SignUpRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.MyPageResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SmsResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SignUpResponse;
import com.whereyouad.WhereYouAd.global.response.DataResponse;
Expand All @@ -13,6 +14,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.RequestBody;

public interface UserControllerDocs {
Expand Down Expand Up @@ -85,4 +87,15 @@ public ResponseEntity<DataResponse<SmsResponse.SmsSentResponse>> sendSms(
})
public ResponseEntity<DataResponse<SmsResponse.SmsVerifiedResponse>> verifySms(
@RequestBody @Valid SmsRequest.SmsVerifyRequest request);

@Operation(
summary = "마이페이지 API",
description = "Authorization : Bearer \\<AccessToken\\> 을 헤더로 받아 현재 로그인한 회원의 정보를 조회합니다.\n\n" +
"회원 DB id,이메일, 이름, 프로필 이미지 URL, 전화번호, 이메일 인증 여부(true / false) 를 반환합니다."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "404_1", description = "해당 사용자 존재하지 않음")
})
public ResponseEntity<DataResponse<MyPageResponse>> getMyPage(@AuthenticationPrincipal(expression = "userId") Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.whereyouad.WhereYouAd.global.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
@EnableCaching
public class RedisConfig {

@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// Redis 캐시 설정 정의
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
// Key 직렬화: String ("user:profile::1" 처럼 보기 좋게 저장)
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// Value 직렬화: JSON (자바 객체 -> JSON 변환 저장)
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
// TTL 설정: 데이터 유효 시간 (30분)
.entryTtl(Duration.ofMinutes(30))
// null 데이터는 캐싱하지 않음
.disableCachingNullValues();

return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
}