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
25 changes: 22 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ dependencies {

//db
implementation 'mysql:mysql-connector-java:8.0.30'

compileOnly 'org.projectlombok:lombok'
implementation 'mysql:mysql-connector-java:8.0.30'
annotationProcessor 'org.projectlombok:lombok'

//test
Expand All @@ -80,6 +78,12 @@ dependencies {
implementation 'io.micrometer:micrometer-registry-prometheus'

implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

//querydsl
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
}

dependencyManagement {
Expand All @@ -102,7 +106,7 @@ jacocoTestReport {
classDirectories.setFrom(
files(
classDirectories.files.collect {
fileTree(dir: it, includes: ['**/application/**', '**/domain/**'])
fileTree(dir: it, includes: ['**/application/**', '**/domain/**'], excludes: ['**/domain/**/exception/**', '**/domain/**/type/**'])
}
)
)
Expand Down Expand Up @@ -137,3 +141,18 @@ bootJar {
into 'static/docs'
}
}

// QueryDSL
def querydslDir = "$buildDir/generated/querydsl"

tasks.withType(JavaCompile).configureEach {
options.generatedSourceOutputDirectory = file(querydslDir)
}

sourceSets {
main {
java {
srcDirs += querydslDir
}
}
}
33 changes: 32 additions & 1 deletion src/docs/asciidoc/auth.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,35 @@ include::{snippets}/auth-controller-test/kakao-logout/http-response.adoc[]

==== 응답

include::{snippets}/auth-controller-test/kakao-logout/response-body.adoc[]
include::{snippets}/auth-controller-test/kakao-logout/response-body.adoc[]

== 토큰 상태 확인

쿠키에 담긴 토큰의 유효성을 확인합니다
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

설명 문장 끝에 마침표가 누락되었습니다.

기존 모든 섹션의 설명문은 마침표로 끝나지만, 이 섹션만 누락되어 있습니다.

📄 수정 제안
-쿠키에 담긴 토큰의 유효성을 확인합니다
+쿠키에 담긴 토큰의 유효성을 확인합니다.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
쿠키에 담긴 토큰의 유효성을 확인합니다
쿠키에 담긴 토큰의 유효성을 확인합니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/docs/asciidoc/auth.adoc` at line 106, The description sentence "쿠키에 담긴
토큰의 유효성을 확인합니다" in the auth.adoc document is missing a period; update that
sentence to end with a full stop (.) so it reads "쿠키에 담긴 토큰의 유효성을 확인합니다." to
match the punctuation style of other section descriptions.


=== Example

include::{snippets}/auth-controller-test/check-auth_-success/http-request.adoc[]

=== HTTP

==== 요청

include::{snippets}/auth-controller-test/check-auth_-success/curl-request.adoc[]
Comment on lines +110 to +116
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

=== Example==== 요청 섹션에서 스니펫 파일이 서로 뒤바뀌었습니다.

파일 내 모든 기존 섹션은 아래 패턴을 따릅니다.

  • === Examplecurl-request.adoc
  • ==== 요청 (HTTP 하위) → http-request.adoc

그러나 새로 추가된 "토큰 상태 확인" 섹션은 두 파일이 반대로 포함되어 있어, 렌더링된 문서에 잘못된 내용이 표시됩니다.

📄 수정 제안
 === Example
 
-include::{snippets}/auth-controller-test/check-auth_-success/http-request.adoc[]
+include::{snippets}/auth-controller-test/check-auth_-success/curl-request.adoc[]
 
 === HTTP
 
 ==== 요청
 
-include::{snippets}/auth-controller-test/check-auth_-success/curl-request.adoc[]
+include::{snippets}/auth-controller-test/check-auth_-success/http-request.adoc[]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
include::{snippets}/auth-controller-test/check-auth_-success/http-request.adoc[]
=== HTTP
==== 요청
include::{snippets}/auth-controller-test/check-auth_-success/curl-request.adoc[]
include::{snippets}/auth-controller-test/check-auth_-success/curl-request.adoc[]
=== HTTP
==== 요청
include::{snippets}/auth-controller-test/check-auth_-success/http-request.adoc[]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/docs/asciidoc/auth.adoc` around lines 110 - 116, The "토큰 상태 확인" section
has the two snippet includes swapped: ensure the `=== Example` section includes
the curl snippet and the `==== 요청` (HTTP subheading) includes the http-request
snippet; specifically swap the includes so
`include::{snippets}/auth-controller-test/check-auth_-success/curl-request.adoc[]`
is under `=== Example` and
`include::{snippets}/auth-controller-test/check-auth_-success/http-request.adoc[]`
is under `==== 요청` to match the project's established pattern.


==== 응답

include::{snippets}/auth-controller-test/check-auth_-success/http-response.adoc[]

=== Body

==== 응답 (valid)

include::{snippets}/auth-controller-test/check-auth_-success/response-body.adoc[]

==== 응답 (invalid)

include::{snippets}/auth-controller-test/check-auth_-expired/response-body.adoc[]

include::{snippets}/auth-controller-test/check-auth_-invalid-token/response-body.adoc[]
Comment on lines +110 to +132
Copy link
Copy Markdown

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:

#!/bin/bash
# Description: 실제 생성된 스니펫 디렉터리 이름을 확인하고, 테스트 메서드명을 조회합니다.

# 생성된 스니펫 디렉터리 확인
echo "=== Snippet directories with 'check-auth' ==="
fd -t d "check-auth" --full-path

# 테스트 클래스에서 check-auth 관련 메서드명 조회
echo -e "\n=== Test methods related to check-auth ==="
rg -n "checkAuth|check_auth|check-auth" --type=java -A 2

Repository: moddo-kr/moddo-backend

Length of output: 1813


🏁 Script executed:

#!/bin/bash
# Description: REST Docs 스니펫 출력 위치 확인 및 실제 디렉터리명 검색

echo "=== 일반적인 REST Docs 출력 디렉터리 검색 ==="
# 일반적으로 build/, target/, build-snippets/ 등에 생성됨
find . -type d -name "*snippet*" -o -type d -name "*rest-docs*" 2>/dev/null | head -20

echo -e "\n=== 'check' 키워드로 관련 디렉터리 검색 ==="
fd -t d "check" --max-depth 5 | grep -v node_modules | head -20

echo -e "\n=== build 디렉터리 내 check-auth 관련 파일/디렉터리 ==="
find build -type d -name "*check-auth*" -o -type d -name "*checkAuth*" 2>/dev/null

Repository: moddo-kr/moddo-backend

Length of output: 176


🏁 Script executed:

#!/bin/bash
# Description: AuthControllerTest.java 전체 내용 조회 - checkAuth 관련 메서드들

cat -n src/test/java/com/dnd/moddo/domain/auth/controller/AuthControllerTest.java | grep -A 1 "void checkAuth"

Repository: moddo-kr/moddo-backend

Length of output: 349


스니펫 디렉터리 경로의 _- 패턴을 수정하세요.

Spring REST Docs는 테스트 메서드명을 디렉터리명으로 변환할 때 언더스코어를 하이픈으로 변환합니다. 현재 문서의 경로들은 올바르지 않습니다:

  • check-auth_-successcheck-auth-success 수정 필요
  • check-auth_-expiredcheck-auth-expired 수정 필요
  • check-auth_-invalid-tokencheck-auth-invalid-token 수정 필요

또한 checkAuth_NoToken() 테스트 메서드도 존재하므로 해당 스니펫 경로(check-auth-no-token)도 문서에 추가하세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/docs/asciidoc/auth.adoc` around lines 110 - 132, Update the snippet
include paths in auth.adoc to match Spring REST Docs' underscore-to-hyphen
conversion: change occurrences of check-auth_-success, check-auth_-expired, and
check-auth_-invalid-token to check-auth-success, check-auth-expired, and
check-auth-invalid-token respectively (i.e., update the
include::{snippets}/auth-controller-test/... paths), and add an include for the
check-auth-no-token snippet to reflect the existing test method
checkAuth_NoToken().


61 changes: 41 additions & 20 deletions src/docs/asciidoc/settlement.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -66,79 +66,100 @@ include::{snippets}/settlement-controller-test/update-account/request-body.adoc[

include::{snippets}/settlement-controller-test/update-account/response-body.adoc[]

== 비밀번호 검증
== 모임 조회

비밀번호를 검증할 수 있습니다.
모임과 참가자를 조회할 수 있습니다.

=== Example

include::{snippets}/settlement-controller-test/is-password-match/curl-request.adoc[]
include::{snippets}/settlement-controller-test/get-settlement/curl-request.adoc[]

=== HTTP

==== 요청

include::{snippets}/settlement-controller-test/is-password-match/http-request.adoc[]
include::{snippets}/settlement-controller-test/get-settlement/http-request.adoc[]

==== 응답

include::{snippets}/settlement-controller-test/is-password-match/http-response.adoc[]
include::{snippets}/settlement-controller-test/get-settlement/http-response.adoc[]

=== Body

==== 응답

include::{snippets}/settlement-controller-test/get-settlement/response-body.adoc[]

== 모임 상단 조회

지출 내역의 상단 부분을 조회할 수 있습니다.

=== Example

include::{snippets}/settlement-controller-test/get-header/curl-request.adoc[]

=== HTTP

==== 요청

include::{snippets}/settlement-controller-test/is-password-match/request-body.adoc[]
include::{snippets}/settlement-controller-test/get-header/http-request.adoc[]

==== 응답

include::{snippets}/settlement-controller-test/is-password-match/response-body.adoc[]
include::{snippets}/settlement-controller-test/get-header/http-response.adoc[]

== 모임 조회
=== Body

모임과 참가자를 조회할 수 있습니다.
==== 응답

include::{snippets}/settlement-controller-test/get-header/response-body.adoc[]

== 모임(정산) 리스트 조회

user가 속한 정산의 리스트를 상태별로 조회할 수 있습니다.

=== Example

include::{snippets}/settlement-controller-test/get-settlement/curl-request.adoc[]
include::{snippets}/settlement-controller-test/search-settlement-list/curl-request.adoc[]

=== HTTP

==== 요청

include::{snippets}/settlement-controller-test/get-settlement/http-request.adoc[]
include::{snippets}/settlement-controller-test/search-settlement-list/http-request.adoc[]

include::{snippets}/settlement-controller-test/search-settlement-list/query-parameters.adoc[]

==== 응답

include::{snippets}/settlement-controller-test/get-settlement/http-response.adoc[]
include::{snippets}/settlement-controller-test/search-settlement-list/http-response.adoc[]

=== Body

==== 응답

include::{snippets}/settlement-controller-test/get-settlement/response-body.adoc[]
include::{snippets}/settlement-controller-test/search-settlement-list/response-body.adoc[]

== 모임 상단 조회
== 공유 링크 리스트 조회

지출 내역의 상단 부분을 조회할 수 있습니다.
user가 속한 정산의 공유 링크 리스트를 조회할 수 있다.

=== Example

include::{snippets}/settlement-controller-test/get-header/curl-request.adoc[]
include::{snippets}/settlement-controller-test/get-share-link-list_-success/curl-request.adoc[]

=== HTTP

==== 요청

include::{snippets}/settlement-controller-test/get-header/http-request.adoc[]
include::{snippets}/settlement-controller-test/get-share-link-list_-success/http-request.adoc[]

==== 응답

include::{snippets}/settlement-controller-test/get-header/http-response.adoc[]
include::{snippets}/settlement-controller-test/get-share-link-list_-success/http-response.adoc[]

=== Body

==== 응답

include::{snippets}/settlement-controller-test/get-header/response-body.adoc[]

include::{snippets}/settlement-controller-test/get-share-link-list_-success/response-body.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@
import com.dnd.moddo.auth.presentation.response.TokenResponse;
import com.dnd.moddo.user.domain.User;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.SecurityException;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
Expand All @@ -27,16 +34,32 @@ public String generateAccessToken(Long id, String role) {
}

public TokenResponse generateToken(User user) {
return generateToken(user.getId(), user.getAuthority().toString(), user.getIsMember());
return generateToken(
user.getId(),
user.getAuthority().toString(),
user.getIsMember()
);
}

public TokenResponse generateToken(Long id, String role, Boolean isMember) {
String accessToken = generateToken(id, role, ACCESS_KEY.getMessage(),
jwtProperties.getAccessExpiration());
String refreshToken = generateToken(id, role, REFRESH_KEY.getMessage(),
jwtProperties.getRefreshExpiration());
String accessToken = generateToken(
id, role,
ACCESS_KEY.getMessage(),
jwtProperties.getAccessExpiration()
);

return new TokenResponse(accessToken, refreshToken, getExpiredTime(), isMember);
String refreshToken = generateToken(
id, role,
REFRESH_KEY.getMessage(),
jwtProperties.getRefreshExpiration()
);

return new TokenResponse(
accessToken,
refreshToken,
getExpiredTime(),
isMember
);
}

public String generateGroupToken(Long groupId) {
Expand All @@ -49,9 +72,7 @@ private String generateToken(Long id, String role, String type, Long exp) {
.setHeaderParam(TYPE.message, type)
.claim(ROLE.getMessage(), role)
.signWith(jwtProperties.getSecretKey(), SignatureAlgorithm.HS256)
.setExpiration(
new Date(System.currentTimeMillis() + exp * 1000)
)
.setExpiration(new Date(System.currentTimeMillis() + exp * 1000))
.compact();
}

Expand All @@ -61,12 +82,71 @@ private String generateGroupToken(Long groupId, String type) {
.setHeaderParam(TYPE.message, type)
.signWith(jwtProperties.getSecretKey(), SignatureAlgorithm.HS256)
.setExpiration(
Date.from(LocalDate.now().plusMonths(1).atStartOfDay(ZoneId.systemDefault()).toInstant())
Date.from(
LocalDate.now()
.plusMonths(1)
.atStartOfDay(ZoneId.systemDefault())
.toInstant()
)
)
.compact();
}

private ZonedDateTime getExpiredTime() {
return ZonedDateTime.now().plusSeconds(jwtProperties.getRefreshExpiration());
return ZonedDateTime.now()
.plusSeconds(jwtProperties.getRefreshExpiration());
}

/**
* 토큰 유효성 검증
*/
public void validate(String token) {
try {
parseClaims(token);
} catch (ExpiredJwtException e) {
throw e;
} catch (SecurityException | MalformedJwtException |
UnsupportedJwtException | IllegalArgumentException e) {
throw new JwtException("Invalid JWT token");
}
}

/**
* userId 추출
*/
public Long getUserId(String token) {
Claims claims = parseClaims(token);
return claims.get(AUTH_ID.getMessage(), Long.class);
}

/**
* role 추출
*/
public String getRole(String token) {
Claims claims = parseClaims(token);
return claims.get(ROLE.getMessage(), String.class);
}

/**
* 토큰 타입 추출 (access / refresh 구분용)
*/
public String getTokenType(String token) {
return (String)parseHeader(token).get(TYPE.message);
}

public Claims parseClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(jwtProperties.getSecretKey())
.build()
.parseClaimsJws(token)
.getBody();
}

private Header<?> parseHeader(String token) {
return Jwts.parserBuilder()
.setSigningKey(jwtProperties.getSecretKey())
.build()
.parseClaimsJws(token)
.getHeader();
}
}
Loading
Loading