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
15 changes: 15 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## #️⃣연관된 이슈

> ex) #이슈번호, #이슈번호

## 📝작업 내용

> 이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지 첨부 가능)

### 스크린샷 (선택)

## 💬리뷰 요구사항(선택)

> 리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요
>
> ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
*.yml

### STS ###
.apt_generated
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ repositories {

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
// implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
// testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/konkuk/chacall/domain/chat/domain/ChatRoom.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ public class ChatRoom {
private Long chatRoomId;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
@JoinColumn(name = "member_id", nullable = false, referencedColumnName = "user_id")
private User member;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
@JoinColumn(name = "owner_id", nullable = false, referencedColumnName = "user_id")
private User owner;
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import jakarta.persistence.*;
import konkuk.chacall.domain.foodtruck.domain.value.*;
import konkuk.chacall.domain.user.User;
import konkuk.chacall.global.common.converter.MenuCategoryListConverter;
import konkuk.chacall.global.common.converter.PhotoUrlListConverter;
import konkuk.chacall.global.common.domain.BaseEntity;
import lombok.AccessLevel;
import lombok.Getter;
Expand Down Expand Up @@ -34,11 +36,11 @@ public class FoodTruck extends BaseEntity {
@Column(nullable = false)
private boolean timeDiscussRequired;

@Convert(converter = PhotoUrlList.class)
@Convert(converter = PhotoUrlListConverter.class)
@Column(nullable = false)
private PhotoUrlList foodTruckPhotoList;

@Convert(converter = MenuCategoryList.class)
@Convert(converter = MenuCategoryListConverter.class)
@Column(nullable = false)
private MenuCategoryList menuCategoryList;

Expand Down
99 changes: 99 additions & 0 deletions src/main/java/konkuk/chacall/domain/test/TestController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package konkuk.chacall.domain.test;

import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import konkuk.chacall.global.common.dto.BaseResponse;
import konkuk.chacall.global.common.exception.AuthException;
import konkuk.chacall.global.common.exception.BusinessException;
import konkuk.chacall.global.common.exception.DomainRuleException;
import konkuk.chacall.global.common.exception.EntityNotFoundException;
import konkuk.chacall.global.common.exception.code.ErrorCode;
import lombok.Getter;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/test")
public class TestController {
Comment thread
buzz0331 marked this conversation as resolved.

@GetMapping("/hello")
public String hello() {
return "Hello, World!";
}

@GetMapping("/ping")
public BaseResponse<String> ping() {
return BaseResponse.ok("pong");
}

// === 커스텀 예외들 ===
@GetMapping("/auth-error")
public String authError() {
throw new AuthException(ErrorCode.AUTH_UNAUTHORIZED);
}
Comment on lines +20 to +34

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

커스텀 예외들을 확인할 수 있는 테스트용 컨트롤러들 만드신 것 좋네요!
프론트 단에서 예외 형식 확인할 때 좀 더 편할 것 같아요


@GetMapping("/entity-error")
public String entityError() {
throw new EntityNotFoundException(ErrorCode.USER_NOT_FOUND);
}

@GetMapping("/domain-error")
public String domainError() {
throw new DomainRuleException(ErrorCode.USER_ALREADY_EXISTS);
}

@GetMapping("/business-error")
public String businessError() {
throw new BusinessException(ErrorCode.USER_NICKNAME_DUPLICATION);
}

@GetMapping("/runtime-error")
public String runtimeError() {
throw new RuntimeException("강제 RuntimeException 발생");
}

// === GlobalExceptionHandler 내장 케이스들 ===

// 1. MethodArgumentNotValidException 테스트 (@Valid DTO 사용)
@PostMapping("/validate-body")
public String validateBody(@Valid @RequestBody UserRequest request) {
return "유효성 통과: " + request.toString();
}

// 2. MethodArgumentTypeMismatchException 테스트
// 호출: /test/type-mismatch?id=문자열
@GetMapping("/type-mismatch")
public String typeMismatch(@RequestParam("id") Long id) {
return "입력한 id: " + id;
}

// 3. MissingServletRequestParameterException 테스트
// 호출 시 /test/missing-param 만 요청 -> user 파라미터 없음
@GetMapping("/missing-param")
public String missingParam(@RequestParam("user") String user) {
return "user: " + user;
}

// 4. ConstraintViolationException 테스트
// 호출: /test/constraint?id=-5
@GetMapping("/constraint")
public String constraint(@RequestParam("id") @Valid @Min(1) int id) {
return "id: " + id;
}

// DTO 내부 유효성 검증용 클래스
@Getter
private static class UserRequest {
@NotBlank(message = "이름은 필수 값입니다.")
private String name;

@Size(min = 5, max = 20, message = "닉네임은 5~20자 사이여야 합니다.")
private String nickname;

@Override
public String toString() {
return "UserRequest{name='" + name + "', nickname='" + nickname + "'}";
}
}
}
4 changes: 2 additions & 2 deletions src/main/java/konkuk/chacall/domain/user/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class User extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id", nullable = false)
private String userId;
private Long userId;
Comment thread
buzz0331 marked this conversation as resolved.

@Column(length = 20, nullable = false)
private String name;
Expand All @@ -32,6 +32,6 @@ public class User extends BaseEntity {
private Gender gender;

@Enumerated(EnumType.STRING)
@Column(length = 1)
@Column(length = 10, nullable = false)
private Role role;
}
35 changes: 35 additions & 0 deletions src/main/java/konkuk/chacall/global/common/dto/BaseResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package konkuk.chacall.global.common.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import lombok.Getter;

@Getter
@JsonPropertyOrder({"isSuccess", "code", "message", "data"})
public class BaseResponse<T> {

@JsonProperty("isSuccess")
private final boolean success;

private final int code;

private final String message;

private final T data;

private BaseResponse(boolean success, int code, String message, T data) {
this.success = success;
this.code = code;
this.message = message;
this.data = data;
}

private BaseResponse(ResponseCode response, T data) {
this(response.isSuccess(), response.getCode(), response.getMessage(), data);
}

public static <T> BaseResponse<T> ok(T data) {
return new BaseResponse<>(SuccessCode.API_SUCCESS, data);
}

}
37 changes: 37 additions & 0 deletions src/main/java/konkuk/chacall/global/common/dto/ErrorResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package konkuk.chacall.global.common.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import lombok.Getter;

@Getter
@JsonPropertyOrder({"isSuccess", "code", "message"})
public class ErrorResponse {

@JsonProperty("isSuccess")
private final boolean success;

private final int code;

private final String message;

private ErrorResponse(boolean success, int code, String message) {
this.success = success;
this.code = code;
this.message = message;
}

private ErrorResponse(ResponseCode response) {
this(response.isSuccess(), response.getCode(), response.getMessage());
}

public static ErrorResponse of(ResponseCode response) {
return new ErrorResponse(response);
}

public static ErrorResponse of(ResponseCode response, String message) {
StringBuilder sb = new StringBuilder();
sb.append(response.getMessage()).append(" ").append(message);
return new ErrorResponse(response.isSuccess(), response.getCode(), sb.toString());
}
}
11 changes: 11 additions & 0 deletions src/main/java/konkuk/chacall/global/common/dto/ResponseCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package konkuk.chacall.global.common.dto;

public interface ResponseCode {
int getCode();

String getMessage();

default boolean isSuccess() {
return this instanceof SuccessCode;
}
}
18 changes: 18 additions & 0 deletions src/main/java/konkuk/chacall/global/common/dto/SuccessCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package konkuk.chacall.global.common.dto;

import konkuk.chacall.global.common.dto.ResponseCode;
import lombok.Getter;

@Getter
public enum SuccessCode implements ResponseCode {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

성공 케이스도 세분화하는 형식이군여 확인했습니다!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

음 공통적으로 HttpStatus 200일 때는 API_SUCCESS를 던져주긴 할건데 ErrorCode와 일관성을 맞추기 위해서 정의해두었습니다!

API_SUCCESS(20000, "요청에 성공했습니다."),
;

private final int code;
private final String message;

SuccessCode(int code, String message) {
this.code = code;
this.message = message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package konkuk.chacall.global.common.exception;

import konkuk.chacall.global.common.exception.code.ErrorCode;
import lombok.Getter;

@Getter
public class AuthException extends RuntimeException {
private final ErrorCode errorCode;

public AuthException(ErrorCode errorCode) {
this.errorCode = errorCode;
}

public AuthException(ErrorCode errorCode, Exception e) {
super(e);
this.errorCode = errorCode;
}
Comment on lines +10 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

예외 메시지 누락: super(message)로 로깅 가시성 확보

현재 RuntimeException에 메시지를 전달하지 않아 스택트레이스에서 원인 파악이 어렵습니다. errorCode.getMessage()를 super로 넘기세요.

-    public AuthException(ErrorCode errorCode) {
-        this.errorCode = errorCode;
-    }
+    public AuthException(ErrorCode errorCode) {
+        super(errorCode.getMessage());
+        this.errorCode = errorCode;
+    }
 
-    public AuthException(ErrorCode errorCode, Exception e) {
-        super(e);
-        this.errorCode = errorCode;
-    }
+    public AuthException(ErrorCode errorCode, Exception e) {
+        super(errorCode.getMessage(), e);
+        this.errorCode = errorCode;
+    }
📝 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
public AuthException(ErrorCode errorCode) {
this.errorCode = errorCode;
}
public AuthException(ErrorCode errorCode, Exception e) {
super(e);
this.errorCode = errorCode;
}
public AuthException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
public AuthException(ErrorCode errorCode, Exception e) {
super(errorCode.getMessage(), e);
this.errorCode = errorCode;
}
🤖 Prompt for AI Agents
In src/main/java/konkuk/chacall/global/common/exception/AuthException.java
around lines 10 to 17, the constructors do not pass the error message to the
RuntimeException super, which reduces stacktrace/message visibility; change the
no-cause constructor to call super(errorCode.getMessage()) and the cause-taking
constructor to call super(errorCode.getMessage(), e) while still assigning
this.errorCode = errorCode.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package konkuk.chacall.global.common.exception;

import konkuk.chacall.global.common.exception.code.ErrorCode;
import lombok.Getter;

@Getter
public class BusinessException extends RuntimeException {
private final ErrorCode errorCode;

public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}

public BusinessException(ErrorCode errorCode, Exception e) {
super(errorCode.getMessage(), e);
this.errorCode = errorCode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package konkuk.chacall.global.common.exception;

import konkuk.chacall.global.common.exception.code.ErrorCode;
import lombok.Getter;

@Getter
public class DomainRuleException extends BusinessException {
public DomainRuleException(ErrorCode errorCode) {
super(errorCode);
}

public DomainRuleException(ErrorCode errorCode, Exception e) {
super(errorCode, e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package konkuk.chacall.global.common.exception;


import konkuk.chacall.global.common.exception.code.ErrorCode;

public class EntityNotFoundException extends BusinessException {

public EntityNotFoundException(ErrorCode errorCode) {
super(errorCode);
}
}
Loading