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 @@ -5,6 +5,7 @@
public record LoginRequest(
@NotBlank(message = "이메일을 입력해주세요.")
String email,

@NotBlank(message = "비밀번호를 입력해주세요.")
String password
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
import jakarta.validation.constraints.Pattern;

public record PasswordResetRequest(
@NotBlank @Email
@NotBlank(message = "이메일을 입력해주세요.")
@Email(message = "유효한 이메일 주소를 입력해주세요.")
String email,

@NotBlank
@NotBlank(message = "리셋 토큰을 입력해주세요.")
String resetToken,

@NotBlank
@NotBlank(message = "새 비밀번호를 입력해주세요.")
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,}$",
message = "비밀번호는 영문, 숫자, 특수문자를 포함하여 8자 이상이어야 합니다.")
String newPassword
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
import jakarta.validation.constraints.NotNull;

public record SocialLoginRequest(
@NotNull UserProvider provider,
@NotBlank String accessToken
@NotNull(message = "소셜 로그인 제공자를 선택해주세요.")
UserProvider provider,

@NotBlank(message = "액세스 토큰을 입력해주세요.")
String accessToken
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
import jakarta.validation.constraints.NotNull;

public record VerificationRequest(
@NotBlank @Email
@NotBlank(message = "이메일을 입력해주세요.")
@Email(message = "유효한 이메일 주소를 입력해주세요.")
String email,

@NotNull
@NotNull(message = "검증 목적을 선택해주세요.")
VerificationPurpose purpose
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
import jakarta.validation.constraints.NotNull;

public record VerificationVerifyRequest(
@NotBlank @Email
@NotBlank(message = "이메일을 입력해주세요.")
@Email(message = "유효한 이메일 주소를 입력해주세요.")
String email,

@NotBlank
@NotBlank(message = "검증 코드를 입력해주세요.")
String code,

@NotNull
@NotNull(message = "검증 목적을 선택해주세요.")
VerificationPurpose purpose
) {
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.loopon.auth.application.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;

public record AuthResult(
@Schema(description = "액세스 토큰 (Bearer)", example = "eyJhbGciOiJIUzI1NiJ9...")
String accessToken,

@Schema(description = "리프레시 토큰", example = "d1f2e3c4b5a6...")
String refreshToken
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
@Component
@RequiredArgsConstructor
public class AppleAuthClientAdapter {
private final RestClient kakaoRestClient;
private final RestClient appleRestClient;
private final AppleClientSecretGenerator secretGenerator;

@Value("${apple.client-id}")
Expand All @@ -33,7 +33,7 @@ public AppleTokenResponse getTokens(String authorizationCode) {
params.add("grant_type", "authorization_code");

try {
return kakaoRestClient.post()
return appleRestClient.post()
.uri("https://appleid.apple.com/auth/token")
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

RestClient에 이미 baseUrl("https://appleid.apple.com")을 설정했는데, 요청에서 다시 절대 URL("https://appleid.apple.com/auth/token")을 사용하고 있습니다. baseUrl과 URI가 분리되면 향후 호스트 변경/환경 분기 시 한쪽만 수정되어 오류가 날 수 있으니, 상대 경로(예: "/auth/token")로 호출해 baseUrl 설정을 일관되게 활용하는 편이 안전합니다.

Suggested change
.uri("https://appleid.apple.com/auth/token")
.uri("/auth/token")

Copilot uses AI. Check for mistakes.
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(params)
Expand Down

This file was deleted.

7 changes: 7 additions & 0 deletions src/main/java/com/loopon/global/config/RestClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ public RestClient kakaoRestClient() {
.build();
}

@Bean("appleRestClient")
public RestClient appleRestClient() {
return RestClient.builder()
.baseUrl("https://appleid.apple.com")
.build();
}

@Bean("geminiRestClient")
public RestClient geminiRestClient() {
return RestClient.builder()
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/loopon/global/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class SecurityConfig {

private static final String[] PUBLIC_URLS = {
"/",
"/health",
"/favicon.ico",
"/v3/api-docs/**",
"/swagger-ui/**",
Expand All @@ -50,7 +51,7 @@ public class SecurityConfig {
private static final String[] API_URLS = {
"/api/users/me",
"/api/users/profile",
"/api/users/password",
"/api/users/password"
};

@Bean
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/com/loopon/global/health/HealthCheckController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.loopon.global.health;

import com.loopon.global.domain.dto.CommonResponse;
import io.swagger.v3.oas.annotations.Hidden;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.Map;

@RestController
@Hidden
@RequiredArgsConstructor
public class HealthCheckController {

@Value("${spring.profiles.active:default}")
private String activeProfile;

@GetMapping("/")
public ResponseEntity<CommonResponse<Map<String, String>>> systemStatus() {
Map<String, String> status = Map.of(
"status", "UP",
"profile", activeProfile,
"serverTime", LocalDateTime.now().toString(),
"message", "LoopOn API Server is running!"
);
Comment on lines +19 to +29
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

GET / 응답에 spring.profiles.active(profile)와 서버 시간(serverTime)을 포함해 외부에 환경 정보를 노출합니다. 운영 환경에서도 퍼블릭 엔드포인트로 열려 있으니, 보안/운영 관점에서 필드 제거(예: status만 반환) 또는 비공개 경로로 분리/권한 제한(예: actuator로 이동, 내부망만 허용 등)을 검토해주세요.

Copilot uses AI. Check for mistakes.

return ResponseEntity.ok(CommonResponse.onSuccess(status));
}

@GetMapping("/health")
public String healthCheck() {
return "OK";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {

private final List<String> excludeUrlPatterns = List.of(
"/",
"/health",
"/favicon.ico",
"/v3/api-docs/**",
"/swagger-ui/**",
Expand All @@ -40,6 +41,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
"/api/users/upload-profile-image",
"/api/auth/login/**",
"/api/auth/reissue",
"/api/auth/logout",
"/api/auth/verification/**"
);

Expand Down
Loading