-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 정산 리스트 조회시 총 정산 금액 응답에 포함하도록 수정 #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
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 | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,7 @@ | ||||||||||||||||||||
| package com.dnd.moddo.auth.presentation; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| import static com.dnd.moddo.auth.infrastructure.security.JwtConstants.*; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||
| import java.util.Collections; | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -100,8 +102,8 @@ public ResponseEntity<AuthCheckResponse> checkAuth( | |||||||||||||||||||
| try { | ||||||||||||||||||||
| Claims claims = jwtProvider.parseClaims(token); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Long userId = jwtProvider.getUserId(token); | ||||||||||||||||||||
| String role = jwtProvider.getRole(token); | ||||||||||||||||||||
| Long userId = claims.get(AUTH_ID.getMessage(), Long.class); | ||||||||||||||||||||
| String role = claims.get(ROLE.getMessage(), String.class); | ||||||||||||||||||||
|
Comment on lines
+105
to
+106
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: # Find build configuration and JJWT version
find . -type f \( -name "*.gradle" -o -name "*.gradle.kts" -o -name "pom.xml" \) | head -20Repository: moddo-kr/moddo-backend Length of output: 98 🏁 Script executed: # Search for JwtProvider class
rg -l "class JwtProvider" --type javaRepository: moddo-kr/moddo-backend Length of output: 139 🏁 Script executed: # Search for AuthCheckResponse class
rg -l "class AuthCheckResponse" --type javaRepository: moddo-kr/moddo-backend Length of output: 48 🏁 Script executed: # Find the target file to confirm lines 105-106
rg -n "Long userId = claims.get" --type java -A 2 -B 2Repository: moddo-kr/moddo-backend Length of output: 593 🏁 Script executed: # Read build.gradle to find JJWT version
cat build.gradle | head -100Repository: moddo-kr/moddo-backend Length of output: 3050 🏁 Script executed: # Find and read JwtProvider class
cat -n src/main/java/com/dnd/moddo/auth/infrastructure/security/JwtProvider.javaRepository: moddo-kr/moddo-backend Length of output: 4937 🏁 Script executed: # Search for AuthCheckResponse
rg -n "class AuthCheckResponse" --type java -A 30Repository: moddo-kr/moddo-backend Length of output: 48 🏁 Script executed: # Search for UserInfo class
rg -n "class UserInfo" --type java -A 20Repository: moddo-kr/moddo-backend Length of output: 48 🏁 Script executed: # Check what happens after lines 105-106 in AuthController
sed -n '105,130p' src/main/java/com/dnd/moddo/auth/presentation/AuthController.javaRepository: moddo-kr/moddo-backend Length of output: 875 🏁 Script executed: # Search more broadly for AuthCheckResponse and UserInfo
rg "AuthCheckResponse\|UserInfo" --type java -lRepository: moddo-kr/moddo-backend Length of output: 48 🏁 Script executed: # Look at the full AuthController file around lines 105
sed -n '80,130p' src/main/java/com/dnd/moddo/auth/presentation/AuthController.javaRepository: moddo-kr/moddo-backend Length of output: 1623 🏁 Script executed: # Search for response classes
fd -e java -path "*/response/*" | xargs grep -l "AuthCheckResponse\|UserInfo" 2>/dev/nullRepository: moddo-kr/moddo-backend Length of output: 236 🏁 Script executed: # Find response classes in the presentation/response directory
find src/main/java/com/dnd/moddo -path "*/response/*" -name "*.java" -type fRepository: moddo-kr/moddo-backend Length of output: 2406 🏁 Script executed: # Search for AuthCheckResponse definition
rg -n "class AuthCheckResponse" --type java -A 50Repository: moddo-kr/moddo-backend Length of output: 48 🏁 Script executed: # Search for UserInfo definition
rg -n "class UserInfo" --type java -A 30Repository: moddo-kr/moddo-backend Length of output: 48 🏁 Script executed: # Read the AuthCheckResponse file directly
cat -n src/main/java/com/dnd/moddo/auth/presentation/response/AuthCheckResponse.javaRepository: moddo-kr/moddo-backend Length of output: 935 🏁 Script executed: # Search for UserInfo more broadly
rg "class UserInfo" --type java -B 2 -A 20Repository: moddo-kr/moddo-backend Length of output: 48 🏁 Script executed: # Search for `@JsonInclude` annotations
rg "@JsonInclude" --type java -B 2 -A 5Repository: moddo-kr/moddo-backend Length of output: 886 🏁 Script executed: # Check if there are any null checks or validation in the auth check endpoint
sed -n '90,140p' src/main/java/com/dnd/moddo/auth/presentation/AuthController.javaRepository: moddo-kr/moddo-backend Length of output: 1496 🏁 Script executed: # Verify JJWT 0.11.5 behavior with null claims
web_search
JJWT 0.11.5 Claims.get() null handling Integer Long type coercionRepository: moddo-kr/moddo-backend Length of output: 267 🌐 Web query:
💡 Result: In JJWT 0.11.5, How to handle it:
Also note: when building tokens in 0.11.5, setting a claim to Sources 🌐 Web query:
💡 Result: This happens because JWT claims are JSON, and when JJWT uses Jackson to deserialize the claims into an untyped Fix options1) Read as Number n = claims.get("accountId", Number.class);
long accountId = n.longValue();2) Configure JJWT’s Jackson ObjectMapper om = new ObjectMapper()
.enable(DeserializationFeature.USE_LONG_FOR_INTS);
JwtParser parser = Jwts.parser()
.json(new JacksonDeserializer(om))
.build();JJWT explicitly supports supplying your own 3) Store the claim as a string (if you control token creation and want to avoid numeric ambiguity) claims.put("accountId", Long.toString(accountId));Sources: JJWT JSON/Jackson customization docs [1], Jackson References: [1] (github.com) [2] (fasterxml.github.io) [3] (stackoverflow.com) Citations:
JJWT 0.11.5에서
null 체크를 추가하세요: 수정 안내 Claims claims = jwtProvider.parseClaims(token);
Long userId = claims.get(AUTH_ID.getMessage(), Long.class);
String role = claims.get(ROLE.getMessage(), String.class);
+if (userId == null || role == null) {
+ return ResponseEntity.ok(
+ AuthCheckResponse.fail(AuthCheckResponse.AuthFailReason.INVALID_TOKEN)
+ );
+}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
|
|
||||||||||||||||||||
| return ResponseEntity.ok( | ||||||||||||||||||||
| AuthCheckResponse.success(userId, role) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -70,12 +70,9 @@ public ResponseEntity<ErrorResponse> handleValidation( | |||||||||||||||||||||||||||||
| MethodArgumentNotValidException e | ||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| String message = e.getBindingResult() | ||||||||||||||||||||||||||||||
| .getFieldErrors() | ||||||||||||||||||||||||||||||
| .stream() | ||||||||||||||||||||||||||||||
| .findFirst() | ||||||||||||||||||||||||||||||
| .map(error -> error.getField() + " : " + error.getDefaultMessage()) | ||||||||||||||||||||||||||||||
| .orElse("잘못된 요청입니다."); | ||||||||||||||||||||||||||||||
| LoggingUtils.warn(e); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| String message = e.getMessage(); | ||||||||||||||||||||||||||||||
|
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.
이 문자열에는 완전한 클래스명, 필드명, 제약 코드, 거부된 값 등이 포함되어 API 응답으로 반환되면 내부 모델 구조가 외부에 노출됩니다(정보 누출). 또한 이전 구현에서 사용자 친화적인 메시지( 모든 필드 오류 메시지를 수집하거나, 단일 필드 오류 메시지를 반환하는 방식으로 수정하는 것을 권장합니다. 🛡️ 수정 제안 `@ExceptionHandler`(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(
MethodArgumentNotValidException e
) {
LoggingUtils.warn(e);
- String message = e.getMessage();
+ String message = e.getBindingResult().getFieldErrors().stream()
+ .map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
+ .collect(java.util.stream.Collectors.joining(", "));
return ResponseEntity.badRequest()
.body(new ErrorResponse(400, message));
}여러 필드 오류를 모두 반환하기 어렵다면, 첫 번째 필드 오류만 추출하는 방식도 가능합니다: - String message = e.getMessage();
+ String message = e.getBindingResult().getFieldErrors().stream()
+ .findFirst()
+ .map(org.springframework.validation.FieldError::getDefaultMessage)
+ .orElse("잘못된 요청입니다.");📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return ResponseEntity.badRequest() | ||||||||||||||||||||||||||||||
| .body(new ErrorResponse(400, message)); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,7 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| package com.dnd.moddo.domain.Member.entity; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import static org.assertj.core.api.Assertions.*; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import static org.mockito.Mockito.*; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import org.junit.jupiter.api.BeforeEach; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import org.junit.jupiter.api.DisplayName; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -10,6 +11,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||
| import com.dnd.moddo.event.domain.member.Member; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import com.dnd.moddo.event.domain.settlement.Settlement; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import com.dnd.moddo.global.support.GroupTestFactory; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import com.dnd.moddo.user.domain.User; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| class MemberTest { | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -74,4 +76,65 @@ void testUpdatePaymentStatus() { | |||||||||||||||||||||||||||||||||||||||||||||||
| assertThat(member.isPaid()).isTrue(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| assertThat(member.getPaidAt()).isNotNull(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @DisplayName("사용자를 정상적으로 할당할 수 있다.") | ||||||||||||||||||||||||||||||||||||||||||||||||
| @Test | ||||||||||||||||||||||||||||||||||||||||||||||||
| void assignUser_success() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // given | ||||||||||||||||||||||||||||||||||||||||||||||||
| Member member = Member.builder() | ||||||||||||||||||||||||||||||||||||||||||||||||
| .name("기존이름") | ||||||||||||||||||||||||||||||||||||||||||||||||
| .settlement(mockSettlement) | ||||||||||||||||||||||||||||||||||||||||||||||||
| .role(ExpenseRole.PARTICIPANT) | ||||||||||||||||||||||||||||||||||||||||||||||||
| .isPaid(false) | ||||||||||||||||||||||||||||||||||||||||||||||||
| .build(); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| User user = mock(User.class); | ||||||||||||||||||||||||||||||||||||||||||||||||
| when(user.getName()).thenReturn("새이름"); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // when | ||||||||||||||||||||||||||||||||||||||||||||||||
| member.assignUser(user); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // then | ||||||||||||||||||||||||||||||||||||||||||||||||
| assertThat(member.getUser()).isEqualTo(user); | ||||||||||||||||||||||||||||||||||||||||||||||||
| assertThat(member.getName()).isEqualTo("새이름"); // 동기화 확인 | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @DisplayName("이미 사용자가 연결되어 있으면 예외가 발생한다.") | ||||||||||||||||||||||||||||||||||||||||||||||||
| @Test | ||||||||||||||||||||||||||||||||||||||||||||||||
| void assignUser_throwException_whenUserAlreadyAssigned() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // given | ||||||||||||||||||||||||||||||||||||||||||||||||
| Member member = Member.builder() | ||||||||||||||||||||||||||||||||||||||||||||||||
| .name("기존이름") | ||||||||||||||||||||||||||||||||||||||||||||||||
| .settlement(mockSettlement) | ||||||||||||||||||||||||||||||||||||||||||||||||
| .role(ExpenseRole.PARTICIPANT) | ||||||||||||||||||||||||||||||||||||||||||||||||
| .isPaid(false) | ||||||||||||||||||||||||||||||||||||||||||||||||
| .build(); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| User firstUser = mock(User.class); | ||||||||||||||||||||||||||||||||||||||||||||||||
| User secondUser = mock(User.class); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| when(firstUser.getName()).thenReturn("첫번째"); | ||||||||||||||||||||||||||||||||||||||||||||||||
| when(secondUser.getName()).thenReturn("두번째"); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| member.assignUser(firstUser); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // when & then | ||||||||||||||||||||||||||||||||||||||||||||||||
| assertThatThrownBy(() -> member.assignUser(secondUser)) | ||||||||||||||||||||||||||||||||||||||||||||||||
| .isInstanceOf(IllegalStateException.class) | ||||||||||||||||||||||||||||||||||||||||||||||||
| .hasMessageContaining("이미 사용자와 연결된 멤버입니다."); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+113
to
+124
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: #!/bin/bash
# Find test configuration and MockitoExtension usage
rg -n --type=java "MockitoExtension|STRICT_STUBS|MockitoSettings" -C2Repository: moddo-kr/moddo-backend Length of output: 41559 🏁 Script executed: #!/bin/bash
# Read the test file to verify the code snippet
find . -name "MemberTest.java" -type f | head -5Repository: moddo-kr/moddo-backend Length of output: 132 🏁 Script executed: #!/bin/bash
# Check for test setup configuration files
find . -name "*.properties" -o -name "*.yml" -o -name "*.yaml" | grep -E "(test|config)" | head -20Repository: moddo-kr/moddo-backend Length of output: 102 🏁 Script executed: #!/bin/bash
# Read the MemberTest.java file to verify the code
cat -n ./src/test/java/com/dnd/moddo/domain/Member/entity/MemberTest.java | sed -n '100,135p'Repository: moddo-kr/moddo-backend Length of output: 1218 🏁 Script executed: #!/bin/bash
# Find the Member entity class
find . -name "Member.java" -type f | grep -E "entity|domain" | grep -v TestRepository: moddo-kr/moddo-backend Length of output: 127 🏁 Script executed: #!/bin/bash
# Check test configuration for Mockito strict stubbing
cat ./src/test/resources/application.yml | head -30Repository: moddo-kr/moddo-backend Length of output: 686 🏁 Script executed: #!/bin/bash
# Read the Member.java file to see assignUser method
cat -n ./src/main/java/com/dnd/moddo/event/domain/member/Member.java | grep -A 20 "assignUser"Repository: moddo-kr/moddo-backend Length of output: 695 🏁 Script executed: #!/bin/bash
# Read MemberTest.java class header to check MockitoExtension
cat -n ./src/test/java/com/dnd/moddo/domain/Member/entity/MemberTest.java | head -30Repository: moddo-kr/moddo-backend Length of output: 1079 🏁 Script executed: #!/bin/bash
# Search for MockitoSettings or Strictness configuration in the test class
rg -n "MockitoSettings|Strictness" ./src/test/java/com/dnd/moddo/domain/Member/entity/MemberTest.javaRepository: moddo-kr/moddo-backend Length of output: 48 불필요한 스텁을 제거하세요. Line 117의 제거 제안 User firstUser = mock(User.class);
User secondUser = mock(User.class);
when(firstUser.getName()).thenReturn("첫번째");
- when(secondUser.getName()).thenReturn("두번째");
member.assignUser(firstUser);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| @DisplayName("null 사용자를 할당하면 예외가 발생한다.") | ||||||||||||||||||||||||||||||||||||||||||||||||
| @Test | ||||||||||||||||||||||||||||||||||||||||||||||||
| void assignUser_throwException_whenUserIsNull() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| Member member = Member.builder() | ||||||||||||||||||||||||||||||||||||||||||||||||
| .name("기존이름") | ||||||||||||||||||||||||||||||||||||||||||||||||
| .settlement(mockSettlement) | ||||||||||||||||||||||||||||||||||||||||||||||||
| .role(ExpenseRole.PARTICIPANT) | ||||||||||||||||||||||||||||||||||||||||||||||||
| .isPaid(false) | ||||||||||||||||||||||||||||||||||||||||||||||||
| .build(); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| assertThatThrownBy(() -> member.assignUser(null)) | ||||||||||||||||||||||||||||||||||||||||||||||||
| .isInstanceOf(NullPointerException.class); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
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.
=== Example과==== 요청섹션의 스니펫 파일이 다른 섹션과 반대로 포함되어 있습니다다른 API 섹션(
게스트 토큰 발급,액세스 토큰 재발급등)은 모두 아래 규칙을 따릅니다:=== Example→curl-request.adoc==== 요청→http-request.adoc그런데
토큰 상태 확인섹션(110, 116번 라인)은 반대로 포함되어 있습니다.📝 수정 제안
📝 Committable suggestion
🤖 Prompt for AI Agents