Skip to content

ElevenHub/HubEleven-common

Repository files navigation

HubEleven Common Library

MSA(Microservices Architecture) 환경에서 사용되는 공통 라이브러리입니다.

개요

  • Group ID: com.github.ElevenHub
  • Artifact ID: HubEleven-common
  • Version: v0.0.1
  • Java Version: 17
  • Spring Boot Version: 3.5.8

설치 방법 (Installation)

Gradle

repositories {
    mavenCentral()
    maven { url 'https://jitpack.io' }
}

dependencies {
    implementation 'com.github.ElevenHub:HubEleven-common:v0.0.1'
}

Maven

<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>

<dependency>
    <groupId>com.github.ElevenHub</groupId>
    <artifactId>HubEleven-common</artifactId>
    <version>v0.0.1</version>
</dependency>

필수 설정 (Configuration)

이 라이브러리의 Bean과 Entity를 인식하기 위해 Main Application Class에 아래 설정이 필요합니다.

package com.example.service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = {
    "com.example.service",      // 현재 서비스 패키지
    "com.commonLib.common"       // 공통 모듈 패키지
})
@EntityScan(basePackages = {
    "com.example.service",      // 현재 서비스 엔티티
    "com.commonLib.common"       // 공통 모듈 엔티티 (BaseEntity)
})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

주요 기능

1. 공통 응답 형식 (API Response)

통일된 API 응답 형식을 제공합니다.

ApiResponse 응답 형식

{
  "code": "SUCCESS",
  "message": "성공했습니다.",
  "result": { ... }
}

ApiResponseEntity 사용 예시

@RestController
@RequestMapping("/api/users")
public class UserController {

    // 성공 응답 (200 OK)
    @GetMapping("/{id}")
    public ResponseEntity<ApiResponse<UserResponse>> getUser(@PathVariable Long id) {
        UserResponse user = userService.getUser(id);
        return ApiResponseEntity.success(user);
    }

    // 생성 응답 (201 Created)
    @PostMapping
    public ResponseEntity<ApiResponse<UserResponse>> createUser(@RequestBody UserRequest request) {
        UserResponse user = userService.createUser(request);
        return ApiResponseEntity.create(SuccessCode.CREATED, "/api/users/" + user.getId(), user);
    }

    // 커스텀 응답
    @PutMapping("/{id}")
    public ResponseEntity<ApiResponse<UserResponse>> updateUser(@PathVariable Long id, @RequestBody UserRequest request) {
        UserResponse user = userService.updateUser(id, request);
        return ApiResponseEntity.from(SuccessCode.UPDATED, user);
    }

    // 단순 메시지 응답
    @DeleteMapping("/{id}")
    public ResponseEntity<ApiResponse<Void>> deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return ApiResponseEntity.ok("사용자가 삭제되었습니다.");
    }
}

2. 페이징 처리

표준화된 페이징 요청/응답을 제공합니다.

CommonPageRequest 사용

컨트롤러에서 CommonPageRequest를 파라미터로 받으면 자동으로 쿼리 파라미터가 바인딩되고, 검증 및 기본값 설정이 처리됩니다.

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping
    public ResponseEntity<ApiResponse<CommonPageResponse<UserResponse>>> getUsers(
            CommonPageRequest pageRequest  // 자동으로 쿼리 파라미터 바인딩
    ) {
        // pageRequest.toPageable()로 Spring Data Pageable 생성
        Page<User> userPage = userService.getUsers(pageRequest.toPageable(), pageRequest.keyword());

        // Entity -> DTO 변환과 함께 페이징 응답 생성
        CommonPageResponse<UserResponse> response = PagingUtils.convert(userPage, UserResponse::from);

        return ApiResponseEntity.success(response);
    }
}

API 요청 예시

GET /api/users?page=0&size=10&sortType=CREATED_AT&direction=DESC&keyword=홍길동

페이징 응답 형식

{
  "code": "SUCCESS",
  "message": "성공했습니다.",
  "result": {
    "content": [
      { "id": 1, "name": "User1" },
      { "id": 2, "name": "User2" }
    ],
    "page": 0,
    "size": 10,
    "totalElements": 100,
    "totalPages": 10,
    "first": true,
    "last": false
  }
}

페이징 파라미터 및 자동 검증

CommonPageRequest의 compact constructor에서 자동으로 검증 및 기본값 설정이 처리됩니다:

  • page: 페이지 번호
    • 음수인 경우 → 자동으로 0으로 변환
  • size: 페이지 크기
    • 10, 30, 50만 허용
    • 다른 값인 경우 → 자동으로 10으로 변환
  • sortType: 정렬 기준
    • 가능한 값: CREATED_AT, UPDATED_AT
    • null인 경우 → 자동으로 CREATED_AT으로 설정
  • direction: 정렬 방향
    • 가능한 값: ASC, DESC
    • null인 경우 → 자동으로 DESC로 설정
  • keyword: 검색 키워드 (선택사항)

3. 예외 처리

전역 예외 처리를 위한 구조를 제공합니다.

Step 1: ErrorCode 정의

각 서비스에서 ErrorCode 인터페이스를 구현하여 도메인별 에러 코드를 정의합니다.

package com.example.service.code;

import com.commonLib.common.code.ErrorCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@RequiredArgsConstructor
public enum UserErrorCode implements ErrorCode {
    USER_NOT_FOUND(HttpStatus.NOT_FOUND, "사용자를 찾을 수 없습니다."),
    USER_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 존재하는 사용자입니다."),
    INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "잘못된 비밀번호입니다.");

    private final HttpStatus httpStatus;
    private final String message;

    @Override
    public String getName() {
        return name();
    }
}

Step 2: GlobalException 사용

@Service
public class UserService {

    public UserResponse getUser(Long id) {
        User user = userRepository.findById(id)
            .orElseThrow(() -> new GlobalException(UserErrorCode.USER_NOT_FOUND));

        return UserResponse.from(user);
    }

    public UserResponse createUser(UserRequest request) {
        if (userRepository.existsByEmail(request.getEmail())) {
            throw new GlobalException(UserErrorCode.USER_ALREADY_EXISTS);
        }

        // 사용자 생성 로직...
    }
}

에러 응답 형식

{
  "code": "USER_NOT_FOUND",
  "message": "사용자를 찾을 수 없습니다.",
  "result": null
}

GlobalExceptionHandler

모든 예외를 자동으로 처리합니다:

  • GlobalException: ErrorCode를 사용한 응답
  • 기타 예외: 500 서버 에러 응답 (CommonErrorCode.SERVER_ERROR)

4. JPA Auditing

엔티티의 생성/수정 정보를 자동으로 관리합니다.

BaseEntity 상속

@Entity
@Getter
public class User extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;

    // createdAt, createdBy, updatedAt, updatedBy, deletedAt, deletedBy는
    // BaseEntity에서 자동으로 관리됩니다.
}

자동 관리되는 필드

필드 타입 설명
createdAt LocalDateTime 생성 일시 (자동 설정, 수정 불가)
createdBy Long 생성자 ID (X-User-Id 헤더에서 추출)
updatedAt LocalDateTime 수정 일시 (자동 갱신)
updatedBy Long 수정자 ID (X-User-Id 헤더에서 추출)
deletedAt LocalDateTime 삭제 일시 (Soft Delete)
deletedBy Long 삭제자 ID

Soft Delete 사용

@Service
public class UserService {

    @Transactional
    public void deleteUser(Long id, Long userId) {
        User user = userRepository.findById(id)
            .orElseThrow(() -> new GlobalException(UserErrorCode.USER_NOT_FOUND));

        // Soft Delete 수행
        user.delete(userId);
        // DB에서 실제로 삭제되지 않고 deletedAt, deletedBy만 설정됨
    }

    public List<User> getActiveUsers() {
        List<User> users = userRepository.findAll();
        // 삭제된 사용자 필터링
        return users.stream()
            .filter(user -> !user.isDeleted())
            .collect(Collectors.toList());
    }
}

사용자 ID 추출 (AuditorAware)

createdByupdatedBy는 HTTP 요청 헤더 X-User-Id에서 자동으로 추출됩니다.

Gateway 또는 각 서비스의 인증 필터에서 다음과 같이 헤더를 설정해야 합니다:

@Component
public class AuthFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        // JWT 토큰에서 사용자 ID 추출
        Long userId = extractUserIdFromToken(request);

        // X-User-Id 헤더에 설정 (내부 서비스 호출 시)
        request.setAttribute("X-User-Id", userId.toString());

        filterChain.doFilter(request, response);
    }
}

5. 공통 코드

SuccessCode

기본 제공되는 성공 코드입니다.

SuccessCode.SUCCESS    // 200 OK, "성공했습니다."
SuccessCode.CREATED    // 201 Created, "생성되었습니다."
SuccessCode.UPDATED    // 200 OK, "수정되었습니다."
SuccessCode.DELETED    // 200 OK, "삭제되었습니다."

CommonErrorCode

기본 제공되는 에러 코드입니다.

CommonErrorCode.SERVER_ERROR  // 500 Internal Server Error, "서버 에러가 발생하였습니다."

유틸리티

PagingUtils

Page 객체를 CommonPageResponse로 변환할 때 사용합니다.

// Entity -> DTO 변환과 함께 페이징 응답 생성
CommonPageResponse<UserDTO> response = PagingUtils.convert(page, UserDTO::from);

// 또는 람다식 사용
CommonPageResponse<UserDTO> response = PagingUtils.convert(page, user -> new UserDTO(user));

버전 관리 (Versioning)

  • v0.0.1: 초기 버전
    • BaseEntity (JPA Auditing)
    • ApiResponse, ApiResponseEntity
    • GlobalException, GlobalExceptionHandler
    • CommonPageRequest, CommonPageResponse
    • PagingUtils
    • SuccessCode, CommonErrorCode

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages