-
Notifications
You must be signed in to change notification settings - Fork 0
Feature : 카카오 로그인 #23
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
Changes from all commits
0289237
e4e2745
6649c2e
1f22df0
51ca0d8
a0a4991
53f7df8
04d2156
2dda392
0b4f4c5
ea5b7e7
70a64d6
9917b77
bbdf4a1
faf307b
8471c76
412d7b4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| version: '3.8' | ||
|
|
||
| services: | ||
| mysql: | ||
| image: mysql:8.3 | ||
| container_name: hanipman-mysql | ||
| restart: always | ||
| environment: | ||
| MYSQL_ROOT_PASSWORD: 1234 | ||
| MYSQL_DATABASE: hanipman | ||
| MYSQL_USER: hanipman | ||
| MYSQL_PASSWORD: 1234 | ||
| TZ: Asia/Seoul | ||
| ports: | ||
| - "3306:3306" | ||
| volumes: | ||
| - mysql_data:/var/lib/mysql | ||
| - ./mysql.cnf:/etc/mysql/conf.d/my.cnf | ||
| - ./initdb/init.sql:/docker-entrypoint-initdb.d/init.sql:ro | ||
|
Comment on lines
+4
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❓ Verification inconclusiveScript executed: #!/bin/bash
# Locate the compose and application config files
echo "=== Finding compose files ==="
find . -name "*compose*.yml" -o -name "*compose*.yaml" | head -20
echo -e "\n=== Finding application config files ==="
find . -name "application*.yml" -o -name "application*.yaml" | head -20Repository: tinybite-2025/tinybite-server Repository: tinybite-2025/tinybite-server stdout: Script executed: #!/bin/bash
# Read the db-compose.yml file
echo "=== db-compose.yml content ==="
cat -n ./db-compose.yml
echo -e "\n=== application.yml content ==="
cat -n ./src/main/resources/application.yml
echo -e "\n=== application-local.yaml content ==="
cat -n ./src/main/resources/application-local.yaml
echo -e "\n=== application-dev.yaml content ==="
cat -n ./src/main/resources/application-dev.yamlRepository: tinybite-2025/tinybite-server Repository: tinybite-2025/tinybite-server stdout: Script executed: #!/bin/bash
# Check for .env file and local setup documentation
echo "=== Looking for .env file ==="
find . -name ".env*" -type f | head -10
echo -e "\n=== Looking for README or setup docs ==="
find . -name "README*" -o -name "SETUP*" -o -name "LOCAL*" | grep -i readme
echo -e "\n=== Checking for docker/compose setup instructions ==="
ls -la ./docker/ 2>/dev/null || echo "docker dir not found"
echo -e "\n=== Checking if .env is in .gitignore ==="
cat .gitignore 2>/dev/null | grep -E "\.env|env" || echo "No .env pattern in .gitignore"Repository: tinybite-2025/tinybite-server Repository: tinybite-2025/tinybite-server stdout: Script executed: #!/bin/bash
# Read the README to understand local setup instructions
echo "=== README.md content ==="
cat -n ./README.mdRepository: tinybite-2025/tinybite-server Repository: tinybite-2025/tinybite-server stdout: Script executed: #!/bin/bash
# Double-check if there's any .env.example or documentation about the expected DB credentials
echo "=== Checking for .env.example or similar template files ==="
find . -name ".env*" -o -name "*env.example*" -o -name "*env.template*" 2>/dev/null | head -20
echo -e "\n=== Checking docker-compose files for env var usage ==="
grep -r "DB_USERNAME\|DB_PASSWORD\|LOCAL_DB" ./docker/ 2>/dev/null || echo "No DB env vars in docker/ files"
echo -e "\n=== Checking if db-compose is referenced in main docker-compose ==="
cat ./docker/docker-compose.common.ymlRepository: tinybite-2025/tinybite-server Repository: tinybite-2025/tinybite-server stdout: Compose의 DB 이름/계정과
🤖 Prompt for AI Agents |
||
| command: | ||
| - --character-set-server=utf8mb4 | ||
| - --collation-server=utf8mb4_unicode_ci | ||
| platform: linux/x86_64 | ||
| entrypoint: ["/bin/sh", "-c", "chmod 644 /etc/mysql/conf.d/my.cnf && docker-entrypoint.sh mysqld"] | ||
| healthcheck: | ||
| test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p1234"] | ||
| interval: 10s | ||
| timeout: 5s | ||
| retries: 5 | ||
|
|
||
| volumes: | ||
| mysql_data: | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| package ita.growin.domain.auth.controller; | ||
|
|
||
| import ita.growin.domain.auth.dto.request.KakaoLoginRequest; | ||
| import ita.growin.domain.auth.dto.request.KakaoSignupRequest; | ||
| import ita.growin.domain.auth.dto.request.RefreshTokenRequest; | ||
| import ita.growin.domain.auth.dto.response.AuthResponse; | ||
| import ita.growin.domain.auth.service.AuthService; | ||
| import ita.growin.global.response.APIResponse; | ||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import jakarta.validation.Valid; | ||
| import java.io.IOException; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.*; | ||
| import org.springframework.web.util.UriComponentsBuilder; | ||
|
|
||
| @Slf4j | ||
| @RestController | ||
| @RequestMapping("/api/v1/auth") | ||
| @RequiredArgsConstructor | ||
| public class AuthController { | ||
|
|
||
| @Value("${kakao.client-id}") | ||
| private String kakaoClientId; | ||
|
|
||
| @Value("${kakao.redirect-uri}") | ||
| private String kakaoRedirectUri; | ||
|
|
||
| private final AuthService authService; | ||
|
|
||
| @GetMapping("/kakao") | ||
| public ResponseEntity<Map<String, String>> kakaoLogin() throws IOException { | ||
| String kakaoAuthUrl = UriComponentsBuilder | ||
| .fromUriString("https://kauth.kakao.com/oauth/authorize") | ||
| .queryParam("client_id", kakaoClientId) | ||
| .queryParam("redirect_uri", kakaoRedirectUri) | ||
| .queryParam("response_type", "code") | ||
| .queryParam("scope", "profile_nickname,account_email") | ||
| .build() | ||
| .toUriString(); | ||
|
|
||
| Map<String, String> response = new HashMap<>(); | ||
| response.put("url", kakaoAuthUrl); | ||
|
|
||
| return ResponseEntity.ok(response); | ||
| } | ||
|
|
||
| @PostMapping("/kakao/signup") | ||
| public ResponseEntity<APIResponse<AuthResponse>> kakaoSignup( | ||
| @Valid @RequestBody KakaoSignupRequest request | ||
| ) { | ||
| AuthResponse response = authService.kakaoSignup(request); | ||
| return ResponseEntity.status(HttpStatus.CREATED) | ||
| .body(APIResponse.success(response)); | ||
| } | ||
|
|
||
| @PostMapping("/kakao/login") | ||
| public ResponseEntity<APIResponse<AuthResponse>> kakaoLogin( | ||
| @Valid @RequestBody KakaoLoginRequest request | ||
| ) { | ||
| AuthResponse response = authService.kakaoLogin(request); | ||
| return ResponseEntity.ok(APIResponse.success(response)); | ||
| } | ||
|
|
||
| @PostMapping("/refresh") | ||
| public ResponseEntity<APIResponse<AuthResponse>> refreshToken( | ||
| @Valid @RequestBody RefreshTokenRequest request | ||
| ) { | ||
| AuthResponse response = authService.refreshToken(request); | ||
| return ResponseEntity.ok(APIResponse.success(response)); | ||
| } | ||
|
|
||
| @PostMapping("/logout") | ||
| public ResponseEntity<APIResponse<Void>> logout( | ||
| @RequestAttribute("userId") Long userId | ||
| ) { | ||
| authService.logout(userId); | ||
| return ResponseEntity.ok(APIResponse.success(null)); | ||
| } | ||
|
Comment on lines
+78
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Search for setAttribute calls related to userId
ast-grep --pattern 'request.setAttribute($_, $_)'Repository: tinybite-2025/tinybite-server Length of output: 202 🏁 Script executed: # Find JwtAuthenticationFilter file
fd -t f "JwtAuthenticationFilter"Repository: tinybite-2025/tinybite-server Length of output: 147 🏁 Script executed: # Search for userId attribute setting in filter or interceptor files
rg "setAttribute.*userId" -t javaRepository: tinybite-2025/tinybite-server Length of output: 199 🏁 Script executed: cat -n src/main/java/ita/growin/domain/auth/security/JwtAuthenticationFilter.javaRepository: tinybite-2025/tinybite-server Length of output: 2504 🏁 Script executed: # Check if there's authentication enforcement on logout endpoint
rg -A 5 -B 5 "logout" src/main/java/ita/growin/domain/auth/controller/AuthController.javaRepository: tinybite-2025/tinybite-server Length of output: 466 🏁 Script executed: # Look for security configuration
fd -t f "SecurityConfig" -o "WebSecurityConfig" -o "*Config*" | grep -i security | head -5Repository: tinybite-2025/tinybite-server Length of output: 226 🏁 Script executed: # Find security configuration files
find . -type f -name "*Config*.java" | grep -i security | head -10Repository: tinybite-2025/tinybite-server Length of output: 203 🏁 Script executed: # Check logout method for security annotations
rg -B 3 "logout\(" src/main/java/ita/growin/domain/auth/controller/AuthController.javaRepository: tinybite-2025/tinybite-server Length of output: 256 🏁 Script executed: # Search for @PreAuthorize on logout
rg "@PreAuthorize|@Secured|@RolesAllowed" src/main/java/ita/growin/domain/auth/controller/AuthController.javaRepository: tinybite-2025/tinybite-server Length of output: 55 🏁 Script executed: cat -n src/main/java/ita/growin/domain/auth/security/SecurityConfig.javaRepository: tinybite-2025/tinybite-server Length of output: 2085 🏁 Script executed: cat -n src/main/java/ita/tinybite/global/config/SecurityConfig.javaRepository: tinybite-2025/tinybite-server Length of output: 1031
The logout endpoint uses Add 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package ita.growin.domain.auth.dto.request; | ||
|
|
||
| import jakarta.validation.constraints.NotBlank; | ||
|
|
||
| import lombok.Getter; | ||
|
|
||
|
|
||
| @Getter | ||
| public class KakaoLoginRequest { | ||
|
|
||
| @NotBlank(message = "Access Token은 필수입니다.") | ||
| private String accessToken; | ||
|
|
||
| private String deviceToken; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package ita.growin.domain.auth.dto.request; | ||
|
|
||
| import ita.growin.domain.user.constant.InterestField; | ||
| import ita.growin.domain.user.constant.Target; | ||
| import ita.growin.domain.user.constant.Work; | ||
| import jakarta.validation.constraints.NotNull; | ||
| import jakarta.validation.constraints.NotBlank; | ||
|
|
||
| import lombok.Getter; | ||
|
|
||
| @Getter | ||
| public class KakaoSignupRequest { | ||
|
|
||
| @NotBlank(message = "Code는 필수입니다.") | ||
| private String code; | ||
|
|
||
| @NotNull(message = "동네 정보는 필수입니다.") | ||
| private String location; | ||
|
|
||
| @NotNull(message = "닉네임은 필수입니다.") | ||
| private String nickname; | ||
|
|
||
| @NotNull(message = "전화번호는 필수입니다.") | ||
| private String phone; | ||
|
|
||
| private String deviceToken; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package ita.growin.domain.auth.dto.request; | ||
|
|
||
| import jakarta.validation.constraints.NotBlank; | ||
| import lombok.Getter; | ||
|
|
||
| @Getter | ||
| public class RefreshTokenRequest { | ||
| @NotBlank(message = "Refresh Token은 필수입니다.") | ||
| private String refreshToken; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package ita.growin.domain.auth.dto.response; | ||
|
|
||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
|
|
||
| @Builder | ||
| @Getter | ||
| public class AuthResponse { | ||
| private String accessToken; | ||
| private String refreshToken; | ||
| private String tokenType; | ||
| private Long expiresIn; | ||
| private UserDto user; | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package ita.growin.domain.auth.dto.response; | ||
|
|
||
| import lombok.Getter; | ||
|
|
||
| @Getter | ||
| public class KakaoAuthToken { | ||
| /** 토큰 요청에 필요한 인가 코드 */ | ||
| private String code; | ||
|
|
||
| /** 인증 실패 시 반환되는 에러 코드 */ | ||
| private String error; | ||
|
|
||
| /** 인증 실패 시 반환되는 에러 메시지 */ | ||
| private String error_description; | ||
|
|
||
| /** CSRF 방지용 state 값 (선택적으로 사용) */ | ||
| private String state; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package ita.growin.domain.auth.dto.response; | ||
|
|
||
| import lombok.Getter; | ||
|
|
||
| @Getter | ||
| public class KakaoTokenResponse { | ||
| /** 토큰 타입 (보통 "bearer") */ | ||
| private String token_type; | ||
|
|
||
| /** 사용자 액세스 토큰 */ | ||
| private String access_token; | ||
|
|
||
| /** 액세스 토큰 만료 시간(초 단위) */ | ||
| private Integer expires_in; | ||
|
|
||
| /** 리프레시 토큰 */ | ||
| private String refresh_token; | ||
|
|
||
| /** 리프레시 토큰 만료 시간(초 단위) */ | ||
| private Integer refresh_token_expires_in; | ||
|
|
||
| /** 인증된 정보 범위(scope). 공백으로 구분됨 */ | ||
| private String scope; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package ita.growin.domain.auth.dto.response; | ||
|
|
||
| import ita.growin.domain.user.constant.*; | ||
| import ita.growin.domain.user.entity.User; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
|
|
||
| import java.time.LocalDateTime; | ||
|
|
||
| @Getter | ||
| @AllArgsConstructor | ||
| @Builder | ||
| public class UserDto { | ||
| private Long userId; | ||
| private String email; | ||
| private String nickname; | ||
| private LoginType type; | ||
| private UserStatus status; | ||
| private String location; | ||
| private String phone; | ||
| private LocalDateTime createdAt; | ||
| private Boolean isNewUser; | ||
|
|
||
| public static UserDto from(User user) { | ||
| return UserDto.builder() | ||
| .userId(user.getUserId()) | ||
| .email(user.getEmail()) | ||
| .nickname(user.getNickname()) | ||
| .type(user.getType()) | ||
| .status(user.getStatus()) | ||
| .phone(user.getPhone()) | ||
| .location(user.getLocation()) | ||
| .createdAt(user.getCreatedAt()) | ||
| .isNewUser(false) | ||
| .build(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
git ls-files | grep -i build.gradleRepository: tinybite-2025/tinybite-server
Length of output: 85
🏁 Script executed:
Repository: tinybite-2025/tinybite-server
Length of output: 2665
jjwt version conflict and duplicate spring-boot-starter-security declaration require immediate fixes.
The build.gradle contains conflicting jjwt versions that will cause classpath conflicts:
io.jsonwebtoken:jjwt-*version0.11.5io.jsonwebtoken:jjwt-*version0.13.0Gradle will select one version, but the mismatched api/impl/jackson combination across versions creates risk of
NoSuchMethodErrorat runtime. Additionally,spring-boot-starter-securityis declared twice (lines 31 and 34).Remove the earlier jjwt 0.11.5 block (lines 35–37) and the duplicate security dependency (line 34) to keep only the 0.13.0 version. Also remove duplicate Lombok declarations at lines 69–70 (already present at lines 42–43).
🤖 Prompt for AI Agents