κΈ°μ
μ§λ¨ μ©μκ±°λ νλ«νΌ λ°±μλ μλ²
com.capdi.backend
βββ domain
β βββ {λλ©μΈλͺ
}
β β βββ controller
β β βββ service
β β βββ repository
β β βββ entity
β β βββ dto
βββ global β κ³΅ν΅ μ€μ λ° μ νΈ
λμ
κ·μΉ
μμ
ν΄λμ€
PascalCase
UserService
λ©μλ / λ³μ
camelCase
createUser
μμ
UPPER_SNAKE_CASE
MAX_RETRY_COUNT
ν
μ΄λΈλͺ
snake_case 볡μν
users, expert_profiles
컬λΌλͺ
snake_case
created_at, business_number
URL
kebab-case
/job-posts, /expert-profiles
Enum κ°
UPPER_SNAKE_CASE
PENDING, APPROVED
λͺ¨λ Entityλ BaseTimeEntity μμ (createdAt, updatedAt μλ κ΄λ¦¬)
@Setter μ¬μ© κΈμ§ β κ° λ³κ²½ μ λλ©μΈ λ©μλ μΆκ°
@NoArgsConstructor(access = AccessLevel.PROTECTED) μ¬μ©
String λμ Enum μ¬μ© (@Enumerated(EnumType.STRING))
@Builder.Default λ‘ κΈ°λ³Έκ° μ§μ
// β
μ¬λ°λ₯Έ μμ
@ Entity
@ Getter
@ NoArgsConstructor (access = AccessLevel .PROTECTED )
@ Builder
public class User extends BaseTimeEntity {
@ Enumerated (EnumType .STRING )
@ Builder .Default
private UserStatus status = UserStatus .ACTIVE ;
}
// β μλͺ»λ μμ
@ Entity
@ Getter
@ Setter // κΈμ§
public class User {
private String status ; // String λμ Enum μ¬μ©
private LocalDateTime createdAt ; // BaseTimeEntity μμμΌλ‘ λ체
}
Request DTO β @Valid + Bean Validation μ΄λ
Έν
μ΄μ
μ¬μ©
Response DTO β @Builder + from() μ μ ν©ν 리 λ©μλ μ¬μ©
ν΄λΌμ΄μΈνΈλ‘λΆν° λ°μΌλ©΄ μ λλ κ°μ Request DTOμμ μ μΈ (μ: status, verificationStatus)
λΉλ°λ²νΈλ password λ‘ λ°κ³ , Serviceμμ μνΈν ν μ μ₯ (passwordHash μ§μ μμ κΈμ§)
// β
Request DTO
@ Getter
public class UserCreateRequest {
@ NotBlank (message = "μ΄λ©μΌμ νμμ
λλ€." )
@ Email
private String email ;
@ NotBlank
@ Size (min = 8 )
private String password ; // νλ¬Έ μμ , Serviceμμ μνΈν
}
// β
Response DTO
@ Getter
@ Builder
public class UserResponse {
public static UserResponse from (User user ) { ... }
}
λͺ¨λ API μλ΅μ ResponseEntity<ApiResponse<T>> ννλ‘ λ°ν
@RequestBody κ° μλ κ²½μ° λ°λμ @Valid ν¨κ» μ¬μ©
// β
μ¬λ°λ₯Έ μμ
@ PostMapping
public ResponseEntity <ApiResponse <UserResponse >> createUser (
@ RequestBody @ Valid UserCreateRequest request ) {
return ResponseEntity .ok (ApiResponse .ok ("μ¬μ©μκ° μμ±λμμ΅λλ€." , userService .createUser (request )));
}
@ GetMapping ("/{id}" )
public ResponseEntity <ApiResponse <UserResponse >> getUser (@ PathVariable Long id ) {
return ResponseEntity .ok (ApiResponse .ok (userService .getUser (id )));
}
ν΄λμ€ λ 벨μ @Transactional(readOnly = true) μ μΈ
μ°κΈ° μμ
λ©μλμλ§ @Transactional λ³λ μ μΈ
IllegalArgumentException λμ CustomException(ErrorCode.XXX) μ¬μ©
// β
μ¬λ°λ₯Έ μμ
@ Service
@ Transactional (readOnly = true )
public class UserService {
@ Transactional
public UserResponse createUser (UserCreateRequest request ) { ... }
public UserResponse getUser (Long id ) {
return userRepository .findById (id )
.orElseThrow (() -> new CustomException (ErrorCode .USER_NOT_FOUND ));
}
}
λͺ¨λ λΉμ¦λμ€ μμΈλ CustomException μ¬μ©
μλ¬ μ½λλ ErrorCode enumμ λ±λ‘ ν μ¬μ©
GlobalExceptionHandler μμ μΌκ΄ μ²λ¦¬
// β
ErrorCode λ±λ‘
USER_NOT_FOUND (HttpStatus .NOT_FOUND , "μ‘΄μ¬νμ§ μλ μ¬μ©μμ
λλ€." ),
// β
μμΈ λ°μ
throw new CustomException (ErrorCode .USER_NOT_FOUND );
// μ±κ³΅
{
"success" : true ,
"message" : " μμ²μ΄ μ±κ³΅νμ΅λλ€." ,
"data" : { ... }
}
// μ€ν¨
{
"success" : false ,
"message" : " μ‘΄μ¬νμ§ μλ μ¬μ©μμ
λλ€." ,
"data" : null
}
main βββ λ°°ν¬ λΈλμΉ
dev βββ κ°λ° ν΅ν© λΈλμΉ
feat/* βββ κΈ°λ₯ κ°λ° (μ: feat/user-auth)
fix/* βββ λ²κ·Έ μμ (μ: fix/bid-price-validation)
νμ
μ€λͺ
feat
μλ‘μ΄ κΈ°λ₯ κ°λ°
fix
λ²κ·Έ μμ
refactor
μ½λ 리ν©ν λ§
chore
λΉλ, μ€μ , μμ‘΄μ± λ±
docs
λ¬Έμ μμ± / μμ
test
ν
μ€νΈ μ½λ μμ±
feat: μ¬μ©μ λ‘κ·ΈμΈ API ꡬν
fix: μ
μ°° κΈμ‘ μ ν¨μ± κ²μ¬ μ€λ₯ μμ
refactor: UserService νΈλμμ
λΆλ¦¬
chore: build.gradle μμ‘΄μ± μΆκ°
docs: README μ½λ 컨벀μ
μμ±
λΌλ²¨
μ€λͺ
μμ
feat
μλ‘μ΄ κΈ°λ₯ κ°λ°
μ¬μ©μ λ‘κ·ΈμΈ API ꡬν
fix
λ²κ·Έ μμ
μ
μ°° κΈμ‘ μ ν¨μ± κ²μ¬ μ€λ₯
refactor
μ½λ 리ν©ν λ§
UserService νΈλμμ
λΆλ¦¬
chore
λΉλ, μ€μ , μμ‘΄μ± λ±
build.gradle μμ‘΄μ± μΆκ°
docs
λ¬Έμ μμ± / μμ
README μ½λ 컨벀μ
μμ±
test
ν
μ€νΈ μ½λ μμ±
UserService λ¨μ ν
μ€νΈ μΆκ°
[feat] μ¬μ©μ λ‘κ·ΈμΈ API ꡬν
[fix] μ
μ°° κΈμ‘ μ ν¨μ± κ²μ¬ μ€λ₯ μμ
[refactor] UserService νΈλμμ
λΆλ¦¬
## π μμ
κ°μ
μ΄ μ΄μμμ 무μμ ꡬν/μμ νλμ§ κ°λ΅ν μ€λͺ
## β
μμ
λ΄μ©
* μΈλΆ μμ
1
* μΈλΆ μμ
2
* μΈλΆ μμ
3
## π κ΄λ ¨ μ΄μ
* closes #μ΄μλ²νΈ (μλ κ²½μ°)
## π μ°Έκ³ μ¬ν
API λͺ
μΈ, ERD, λμμΈ λ§ν¬ λ± μ°Έκ³ ν λ΄μ© (μμΌλ©΄ μλ΅)
PR μ λͺ©μ μ΄μ μ λͺ©κ³Ό λμΌ νκ² μμ±
PR λ³Έλ¬Έμ closes #μ΄μλ²νΈ λͺ
μ β μλ μ΄μ λ«κΈ° νμ±ν
PRμ λ°λμ dev λΈλμΉ λ‘ λ¨Έμ§
λΈλμΉ β μ΄μ μ°κ²°
λΈλμΉλͺ
μ μ΄μ λ²νΈλ₯Ό ν¬ν¨ν΄ μΆμ μ μ©μ΄νκ² νλ€.
feat/{μ΄μλ²νΈ}-{μμ
λͺ
}
fix/{μ΄μλ²νΈ}-{μμ
λͺ
}
μμ)
feat/23-user-login
fix/47-bid-price-validation