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
12 changes: 12 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'

//security
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
implementation 'org.springframework.boot:spring-boot-starter-validation'

Comment on lines 31 to +39
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:

git ls-files | grep -i build.gradle

Repository: tinybite-2025/tinybite-server

Length of output: 85


🏁 Script executed:

cat -n build.gradle

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:

  • Lines 35–37: io.jsonwebtoken:jjwt-* version 0.11.5
  • Lines 55–57: io.jsonwebtoken:jjwt-* version 0.13.0

Gradle will select one version, but the mismatched api/impl/jackson combination across versions creates risk of NoSuchMethodError at runtime. Additionally, spring-boot-starter-security is 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
In build.gradle around lines 31 to 39, the file declares
spring-boot-starter-security twice and an older jjwt 0.11.5 trio that conflicts
with the later 0.13.0 entries; remove the duplicate security declaration (the
second spring-boot-starter-security) and delete the
io.jsonwebtoken:jjwt-api/impl/jackson entries for version 0.11.5 so only the
0.13.0 jjwt artifacts remain; additionally remove the duplicate Lombok
declarations found at lines ~69–70 (they are already declared earlier) to avoid
redundant dependency declarations.


// util
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
Expand All @@ -56,6 +64,10 @@ dependencies {
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'com.h2database:h2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

//lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}

tasks.named('test') {
Expand Down
32 changes: 32 additions & 0 deletions db-compose.yml
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
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

❓ Verification inconclusive

Script 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 -20

Repository: tinybite-2025/tinybite-server


Repository: tinybite-2025/tinybite-server
Exit code: 0

stdout:

=== Finding compose files ===
./docker/docker-compose.blue.yml
./docker/docker-compose.common.yml
./docker/docker-compose.green.yml
./db-compose.yml

=== Finding application config files ===
./src/main/resources/application-dev.yaml
./src/main/resources/application-local.yaml
./src/main/resources/application.yaml
./src/main/resources/application.yml
./src/test/resources/application-test.yaml

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.yaml

Repository: tinybite-2025/tinybite-server


Repository: tinybite-2025/tinybite-server
Exit code: 0

stdout:

=== db-compose.yml content ===
     1	version: '3.8'
     2	
     3	services:
     4	  mysql:
     5	    image: mysql:8.3
     6	    container_name: hanipman-mysql
     7	    restart: always
     8	    environment:
     9	      MYSQL_ROOT_PASSWORD: 1234
    10	      MYSQL_DATABASE: hanipman
    11	      MYSQL_USER: hanipman
    12	      MYSQL_PASSWORD: 1234
    13	      TZ: Asia/Seoul
    14	    ports:
    15	      - "3306:3306"
    16	    volumes:
    17	      - mysql_data:/var/lib/mysql
    18	      - ./mysql.cnf:/etc/mysql/conf.d/my.cnf
    19	      - ./initdb/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    20	    command:
    21	      - --character-set-server=utf8mb4
    22	      - --collation-server=utf8mb4_unicode_ci
    23	    platform: linux/x86_64
    24	    entrypoint: ["/bin/sh", "-c", "chmod 644 /etc/mysql/conf.d/my.cnf && docker-entrypoint.sh mysqld"]
    25	    healthcheck:
    26	      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p1234"]
    27	      interval: 10s
    28	      timeout: 5s
    29	      retries: 5
    30	
    31	volumes:
    32	  mysql_data:
=== application.yml content ===
     1	spring:
     2	  application:
     3	    name: growin
     4	
     5	  profiles:
     6	    group:
     7	      local: "local"
     8	      dev: "dev"
     9	      test: "test"
    10	
    11	  datasource:
    12	    driver-class-name: com.mysql.cj.jdbc.Driver
    13	    url: jdbc:mysql://localhost:3306/growin?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
    14	    username: ${DB_USERNAME}
    15	    password: ${DB_PASSWORD}
    16	
    17	  jpa:
    18	    hibernate:
    19	      ddl-auto: update  # 개발: update, 운영: validate 또는 none
    20	    properties:
    21	      hibernate:
    22	        format_sql: true
    23	        show_sql: true
    24	        dialect: org.hibernate.dialect.MySQLDialect
    25	    show-sql: true
    26	
    27	
    28	jwt:
    29	  secret: ${JWT_SECRET}
    30	  access-token-validity: ${JWT_ACCESS_TOKEN_VALIDITY}
    31	  refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY}
    32	
    33	kakao:
    34	  client-id: ${KAKAO_CLIENT_ID}
    35	  redirect-uri: ${KAKAO_REDIRECT_URI}
    36	
    37	logging:
    38	  level:
    39	    org.hibernate.SQL: debug

=== application-local.yaml content ===
     1	spring:
     2	  config:
     3	    import: optional:file:.env[.properties]
     4	    activate:
     5	      on-profile: "local"
     6	  datasource:
     7	    driver-class-name: com.mysql.cj.jdbc.Driver
     8	    url: ${LOCAL_DB_URL}
     9	    username: ${LOCAL_DB_USERNAME}
    10	    password: ${LOCAL_DB_PASSWORD}
    11	
    12	  jpa:
    13	    show-sql: true
    14	    open-in-view: false
    15	    hibernate:
    16	      ddl-auto: create-drop

=== application-dev.yaml content ===
     1	spring:
     2	  config:
     3	    activate:
     4	      on-profile: "dev"
     5	
     6	  datasource:
     7	    driver-class-name: com.mysql.cj.jdbc.Driver
     8	    url: ${DEV_DB_URL}
     9	    username: ${DEV_DB_USERNAME}
    10	    password: ${DEV_DB_PASSWORD}

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
Exit code: 0

stdout:

=== Looking for .env file ===

=== Looking for README or setup docs ===
./README.md

=== Checking for docker/compose setup instructions ===
total 12
drwxr-xr-x 2 jailuser jailuser 100 Dec  2 13:57 .
drwxr-xr-x 8 jailuser jailuser 380 Dec  2 13:58 ..
-rw-r--r-- 1 jailuser jailuser 271 Dec  2 13:57 docker-compose.blue.yml
-rw-r--r-- 1 jailuser jailuser 794 Dec  2 13:57 docker-compose.common.yml
-rw-r--r-- 1 jailuser jailuser 273 Dec  2 13:57 docker-compose.green.yml

=== Checking if .env is in .gitignore ===
.env

Script executed:

#!/bin/bash
# Read the README to understand local setup instructions
echo "=== README.md content ==="
cat -n ./README.md

Repository: tinybite-2025/tinybite-server


Repository: tinybite-2025/tinybite-server
Exit code: 0

stdout:

=== README.md content ===
     1	### 커밋 컨벤션
     2	[Conventional Commits](https://www.conventionalcommits.org/) 규칙을 기반으로 합니다.
     3	
     4	- `feat`: 새로운 기능 추가
     5	- `fix`: 버그 수정
     6	- `docs`: 문서 수정 (README, 주석 등)
     7	- `style`: 코드 포맷팅, 세미콜론 누락 등 (코드 변경 없음)
     8	- `refactor`: 코드 리팩토링 (동작 변화 없음)
     9	- `test`: 테스트 코드 추가/수정
    10	- `chore`: 빌드 업무, 패키지 매니저 수정 등
    11	- `perf`: 성능 개선
    12	
    13	**예시**
    14	
    15	feat: 로그인 API 구현<br>
    16	fix: 회원가입 시 이메일 중복 체크 로직 수정<br>
    17	docs: README 브랜치 전략 추가<br>
    18	refactor: UserService 의존성 주입 방식 변경<br>
    19	
    20	
    21	---
    22	
    23	###  브랜치 전략
    24	`main` / `develop` 2개의 메인 브랜치를 유지하며, 기능 단위 브랜치를 활용합니다.
    25	
    26	- `main`  
    27	  실제 배포용 브랜치 (항상 안정적인 상태 유지)
    28	
    29	- `develop`  
    30	  다음 배포를 준비하는 브랜치 (기능 병합 대상)
    31	
    32	- `feature/{이슈번호-기능명}`  
    33	  새로운 기능 개발 브랜치  
    34	  ex) `feature/12-login-api`
    35	
    36	- `fix/{이슈번호-버그내용}`  
    37	  버그 수정 브랜치  
    38	  ex) `fix/34-email-validation`
    39	
    40	- `hotfix/{버그내용}`  
    41	  운영 중 긴급 버그 수정 브랜치  
    42	  ex) `hotfix/token-expire-bug`
    43	
    44	---
    45	
    46	### Git Flow
    47	1. 기능 개발 시 `develop`에서 `feature/*` 브랜치 생성
    48	2. 기능 개발 완료 후 `develop`으로 PR → 코드 리뷰 → 머지
    49	3. 배포 준비 시 `develop` → `main` 머지
    50	4. 운영 중 긴급 수정은 `main`에서 `hotfix/*` 생성 후
    51	    - 수정 완료 → `main` & `develop` 모두에 반영
    52	
    53	---
    54	
    55	### 커밋 & 브랜치 네이밍 규칙
    56	- 커밋 메시지: `type: 설명` 형식 (한글/영문 가능, 소문자 권장)
    57	- 브랜치 네이밍: `{type}/{이슈번호-설명}` (영문 kebab-case)
    58	
    59	---
    60	
    61	👉 위 전략을 지켜 협업 시 **일관성**과 **가독성**을 보장합니다.

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.yml

Repository: tinybite-2025/tinybite-server


Repository: tinybite-2025/tinybite-server
Exit code: 0

stdout:

=== Checking for .env.example or similar template files ===

=== Checking docker-compose files for env var usage ===
No DB env vars in docker/ files

=== Checking if db-compose is referenced in main docker-compose ===
services:
  nginx:
    container_name: nginx
    image: nginx:1.27.4
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /home/ubuntu/tinybite/nginx/default.conf:/etc/nginx/conf.d/default.conf
      - /home/ubuntu/tinybite/data/certbot/conf:/etc/letsencrypt
      - /home/ubuntu/tinybite/data/certbot/www:/var/www/certbot
    restart: always
    networks:
      - tinybite-network

  certbot:
    container_name: certbot
    image: certbot/certbot
    volumes:
      - /home/ubuntu/tinybite/data/certbot/conf:/etc/letsencrypt
      - /home/ubuntu/tinybite/data/certbot/www:/var/www/certbot
    entrypoint: /bin/sh -c "trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;"
    networks:
      - tinybite-network

networks:
  tinybite-network:
    external: true

Compose의 DB 이름/계정과 application.yml 설정 불일치

db-compose.yml에서는 MYSQL_DATABASE: hanipman, MYSQL_USER: hanipman, MYSQL_PASSWORD: 1234를 사용하지만, application.yml의 JDBC URL은 jdbc:mysql://localhost:3306/growin으로 DB 이름이 growin으로 하드코딩되어 있습니다. 로컬에서 이 compose를 그대로 띄우면 growin 스키마가 존재하지 않아 연결 실패가 발생합니다.

application-local.yaml는 환경변수로 전체 설정을 받도록 되어 있으나, 이를 사용해야 한다는 문서화가 없고 .env.example도 제공되지 않아 개발자가 올바른 로컬 설정을 알기 어렵습니다.

  • DB 이름을 growin으로 맞추거나
  • 애플리케이션 설정을 hanipman으로 맞추거나
  • 로컬 개발 시 사용할 프로필과 환경변수 설정을 문서화하는 것을 권장드립니다.
🤖 Prompt for AI Agents
In db-compose.yml around lines 4-19 the MySQL database name and credentials
(MYSQL_DATABASE=hanipman, MYSQL_USER=hanipman, MYSQL_PASSWORD=1234) do not match
the application's hardcoded JDBC URL (jdbc:mysql://localhost:3306/growin); fix
by either aligning the compose file or the app config: 1) change MYSQL_DATABASE
to growin (and adjust MYSQL_USER/PASSWORD if needed), or 2) update
application.yml JDBC URL to use hanipman and matching credentials, or 3) keep
compose as-is but document using the application-local.yaml profile and provide
a .env.example listing required environment variables (DB name, user, password,
URL, and active profile) so local developers know which profile/env to use.

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
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 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 java

Repository: tinybite-2025/tinybite-server

Length of output: 199


🏁 Script executed:

cat -n src/main/java/ita/growin/domain/auth/security/JwtAuthenticationFilter.java

Repository: 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.java

Repository: 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 -5

Repository: tinybite-2025/tinybite-server

Length of output: 226


🏁 Script executed:

# Find security configuration files
find . -type f -name "*Config*.java" | grep -i security | head -10

Repository: 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.java

Repository: 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.java

Repository: tinybite-2025/tinybite-server

Length of output: 55


🏁 Script executed:

cat -n src/main/java/ita/growin/domain/auth/security/SecurityConfig.java

Repository: tinybite-2025/tinybite-server

Length of output: 2085


🏁 Script executed:

cat -n src/main/java/ita/tinybite/global/config/SecurityConfig.java

Repository: tinybite-2025/tinybite-server

Length of output: 1031


@RequestAttribute("userId") requires authentication enforcement

The logout endpoint uses @RequestAttribute("userId") but the SecurityConfig permits all requests to /api/v1/auth/** (line 33 in SecurityConfig.java). The JwtAuthenticationFilter only sets the userId attribute when a valid token is present; unauthenticated requests bypass this and will trigger MissingRequestValueException.

Add @PreAuthorize("hasAnyRole('USER')") to the logout method or modify the security configuration to require authentication for the /logout path.

🤖 Prompt for AI Agents
In src/main/java/ita/growin/domain/auth/controller/AuthController.java around
lines 78-84, the logout method uses @RequestAttribute("userId") but the endpoint
is currently permitted to all, causing MissingRequestValueException for
unauthenticated calls; fix by securing the endpoint either by adding
@PreAuthorize("hasAnyRole('USER')") to the logout method (and import
org.springframework.security.access.prepost.PreAuthorize) so only authenticated
users with ROLE_USER can call it, or by updating SecurityConfig to require
authentication for the specific /api/v1/auth/logout path (ensure the
JwtAuthenticationFilter runs and sets the userId before controller invocation).

}

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;
}
38 changes: 38 additions & 0 deletions src/main/java/ita/growin/domain/auth/dto/response/UserDto.java
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();
}
}
Loading