# Bio Backend

## 기술 스택

- **Framework**: Spring Boot
- **Database**: PostgreSQL
- **ORM**: Spring Data JPA + QueryDSL
- **Security**: Spring Security + JWT
- **Build Tool**: Gradle
- **Container**: Docker + Kubernetes
- **API Documentation**: Swagger (SpringDoc OpenAPI)

## 개발 환경 설정

### Eclipse/STS 사용 시 필수 설정

**Annotation Processing 설정이 필요합니다.**

STS에서 프로젝트를 열었을 때 컴파일 오류가 발생하는 경우, 다음 설정을 확인하세요:

1. **프로젝트 우클릭** → **Properties** 선택
2. **Java Compiler** → **Annotation Processing** 선택
3. 다음 설정을 확인/적용:
   - **Enable project specific settings** 체크
   - **Enable annotation processing** 체크
   - **Enable processing in editor** 체크 해제 (선택사항)
   - **Generated source directory**: `build/generated/sources/annotationProcessor/java/main`
   - **Generated test source directory**: `build/generated/sources/annotationProcessor/java/test`

**설정 이유:**
- MapStruct 매퍼 인터페이스의 구현체가 자동 생성됩니다
- QueryDSL Q클래스들이 자동 생성됩니다
- 이 설정이 없으면 컴파일 오류가 발생할 수 있습니다

## 개발 가이드

### 1. 프로젝트 구조

```
src/main/java/com/bio/bio_backend/
├── domain/                    # 도메인별 패키지
│   └── base/                 # 기본 도메인
│       └── member/           # 회원 도메인
│           ├── controller/    # API 엔드포인트
│           ├── service/       # 비즈니스 로직
│           ├── repository/    # 데이터 접근
│           ├── entity/        # JPA 엔티티
│           ├── dto/          # 데이터 전송 객체
│           ├── mapper/       # MapStruct 매퍼
│           └── enums/        # 도메인 열거형
├── global/                    # 공통 설정
│   ├── config/               # 설정 클래스
│   ├── security/             # 보안 설정
│   ├── exception/            # 예외 처리
│   ├── aop/                  # AOP 로깅
│   ├── filter/               # HTTP 로깅 필터
│   ├── utils/                # 유틸리티
│   ├── constants/            # 상수 정의
│   ├── dto/                  # 공통 DTO
│   ├── entity/               # 공통 엔티티
│   └── annotation/           # 커스텀 어노테이션
├── BioBackendApplication.java
└── ServletInitializer.java
```

### 2. API 응답 표준화 (ApiResponseDto)

#### 응답 구조

모든 API 응답은 `ApiResponseDto<T>` 형태로 표준화되어 있습니다.

```java
public class ApiResponseDto<T> {
    private boolean success;    // 성공/실패 여부 (true/false)
    private int code;           // HTTP 상태 코드
    private String message;     // 응답 메시지 (ApiResponseCode enum 값)
    private String description; // 응답 설명
    private T data;            // 실제 데이터 (제네릭 타입)
}
```

#### 응답 예시

**성공 응답 (201 Created)**

```json
{
  "success": true,
  "code": 201,
  "message": "COMMON_SUCCESS_CREATED",
  "description": "성공적으로 생성되었습니다",
  "data": {
    "seq": 1,
    "userId": "user123",
    "name": "홍길동"
  }
}
```

**실패 응답 (409 Conflict)**

```json
{
  "success": false,
  "code": 409,
  "message": "USER_ID_DUPLICATE",
  "description": "이미 존재하는 사용자 ID입니다"
}
```

#### 사용 방법

**Controller에서 응답 생성**

```java
@PostMapping("/members")
public ResponseEntity<ApiResponseDto<CreateMemberResponseDto>> createMember(@RequestBody CreateMemberRequestDto requestDto) {
    // ... 비즈니스 로직 ...

    // 성공 응답
    ApiResponseDto<CreateMemberResponseDto> apiResponse =
        ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_CREATED, responseDto);

    return ResponseEntity.status(HttpStatus.CREATED).body(apiResponse);
}
```

**공용 응답 코드 사용**

```java
// ApiResponseCode.java
public enum ApiResponseCode {
    // 공용 성공 코드
    COMMON_SUCCESS(HttpStatus.OK.value(), "요청 성공"),
    COMMON_SUCCESS_CREATED(HttpStatus.CREATED.value(), "성공적으로 생성되었습니다"),
    COMMON_SUCCESS_UPDATED(HttpStatus.OK.value(), "성공적으로 수정되었습니다"),
    COMMON_SUCCESS_DELETED(HttpStatus.OK.value(), "성공적으로 삭제되었습니다"),
    COMMON_SUCCESS_RETRIEVED(HttpStatus.OK.value(), "성공적으로 조회되었습니다"),

    // 공용 오류 코드
    COMMON_BAD_REQUEST(HttpStatus.BAD_REQUEST.value(), "필수 요청 본문이 누락되었거나 오류가 발생했습니다"),
    COMMON_UNAUTHORIZED(HttpStatus.UNAUTHORIZED.value(), "인증에 실패했습니다"),
    COMMON_FORBIDDEN(HttpStatus.FORBIDDEN.value(), "접근이 거부되었습니다"),
    COMMON_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "리소스를 찾을 수 없습니다"),
    COMMON_CONFLICT(HttpStatus.CONFLICT.value(), "충돌이 발생했습니다"),
    COMMON_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR.value(), "서버에서 오류가 발생했습니다"),

    // 사용자 관련 코드
    USER_ID_DUPLICATE(HttpStatus.CONFLICT.value(), "이미 존재하는 사용자 ID입니다"),
    USER_NOT_FOUND(HttpStatus.UNAUTHORIZED.value(), "사용자를 찾을 수 없습니다. 인증에 실패했습니다"),
    USER_PASSWORD_INVALID(HttpStatus.UNAUTHORIZED.value(), "비밀번호가 올바르지 않습니다")
}
```

#### 핵심 규칙

- **모든 API 응답**: `ApiResponseDto<T>`로 감싸서 반환
- **공용 응답 코드**: `COMMON_` 접두사로 시작하는 범용 코드 사용
- **일관된 구조**: `success`, `code`, `message`, `description`, `data` 필드로 표준화
- **성공/실패 구분**: `success` 필드로 명확한 성공/실패 여부 전달
- **제네릭 활용**: `<T>`를 통해 다양한 데이터 타입 지원

### 3. JWT 인증 시스템

#### 토큰 구성

- **Access Token** - 쿠키에 저장, 자동 갱신
- **Refresh Token** - 쿠키에 저장, 자동 갱신

#### 자동 토큰 갱신

**모든 API 요청마다 자동으로 처리됩니다:**

1. **Access Token 유효**: 정상 요청 처리
2. **Access Token 만료**: Refresh Token으로 자동 갱신
3. **새 Access Token 발급**: 응답 헤더에 자동 설정
4. **Refresh Token 갱신**: 보안을 위해 매번 새로운 토큰 발급

#### 주요 컴포넌트

- **`JwtTokenIssuanceFilter`**: 로그인 시 토큰 발급
- **`JwtTokenValidationFilter`**: 요청마다 토큰 검증 및 갱신
- **`JwtUtils`**: 토큰 생성, 검증, 갱신 유틸리티

### 4. API 문서화 (Swagger)

#### Swagger UI 접속

- **URL**: `http://localhost:8080/service/swagger-ui.html`
- **API Docs**: `http://localhost:8080/service/api-docs`

### 5. 시스템 모니터링 (Actuator)

#### Health 체크

- **기본 Health**: `http://localhost:8080/service/actuator/health`
- **Readiness Probe**: `http://localhost:8080/service/actuator/health/readiness`
- **Liveness Probe**: `http://localhost:8080/service/actuator/health/liveness`

#### 시스템 정보

- **애플리케이션 정보**: `http://localhost:8080/service/actuator/info`
- **환경 설정**: `http://localhost:8080/service/actuator/env`
- **설정 속성**: `http://localhost:8080/service/actuator/configprops`
- **메트릭**: `http://localhost:8080/service/actuator/metrics`

#### 주요 어노테이션

```java
@Tag(name = "Member", description = "회원 관리 API")
@Operation(summary = "회원 가입", description = "새로운 회원을 등록합니다.")
@ApiResponses(value = {
    @ApiResponse(responseCode = "201", description = "회원 가입 성공"),
    @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터",
                content = @Content(schema = @Schema(implementation = ApiResponseDto.class))),
    @ApiResponse(responseCode = "409", description = "중복된 사용자 정보",
                content = @Content(schema = @Schema(implementation = ApiResponseDto.class)))
})
```

#### 설정 파일

- **SwaggerConfig.java**: OpenAPI 기본 정보 설정
- **application.properties**: Swagger UI 커스터마이징

### 6. 트랜잭션 관리

#### 기본 설정

```java
@Service
@Transactional(readOnly = true)  // 클래스 레벨: 읽기 전용 기본값
public class MemberServiceImpl {

    // 읽기 전용 메서드 (별도 어노테이션 불필요)
    public MemberDto selectMember(long seq) { ... }

    // 쓰기 작업 메서드 (개별 @Transactional 적용)
    @Transactional
    public MemberDto createMember(MemberDto dto) { ... }
}
```

#### 핵심 규칙

- **클래스 레벨**: `@Transactional(readOnly = true)` 기본 설정
- **메서드별**: 데이터 수정 시에만 `@Transactional` 개별 적용
- **설정**: `spring.jpa.open-in-view=false` (성능 최적화)

### 7. 오류 등록 및 사용

#### 오류 코드 등록

```java
// ApiResponseCode.java
public enum ApiResponseCode {
    USER_ID_DUPLICATE(HttpStatus.CONFLICT.value(), "User ID already exists"),
}
```

#### 오류 사용 방법

```java
// Service에서 예외 발생
throw new ApiException(ApiResponseCode.USER_ID_DUPLICATE);

// Controller에서 예외 처리 (자동)
// GlobalExceptionHandler가 ApiException을 잡아서 응답 변환
```

#### 핵심 규칙

- **오류 코드**: `ApiResponseCode` enum에 모든 오류 정의
- **예외 클래스**: `ApiException`으로 비즈니스 로직 예외 처리
- **자동 처리**: `GlobalExceptionHandler`가 일관된 응답 형태로 변환

### 8. 로깅 시스템

#### @LogExecution 어노테이션 사용법

**메서드 실행 로깅을 위한 필수 어노테이션입니다.**

##### 기본 사용법

```java
@LogExecution("회원 등록")
@PostMapping("/register")
public ResponseEntity<ApiResponseDto<CreateMemberResponseDto>> createMember(@RequestBody CreateMemberRequestDto requestDto) {
    // 메서드 실행 시 자동으로 로그 출력
}
```

##### 로그 출력 예시

**메서드 시작 시:**

```
[START] 회원 등록 | 호출경로: MemberController.createMember | 사용자: 고명빈(kmbin92)
```

**메서드 성공 시:**

```
[SUCCESS] 회원 등록 | 호출경로: MemberController.createMember | 사용자: 고명빈(kmbin92) | 시간: 200ms
```

**메서드 실패 시:**

```
[FAILED] 회원 등록 | 호출경로: MemberController.createMember | 사용자: 고명빈(kmbin92) | 시간: 23ms | 오류: 이미 존재하는 사용자 ID입니다
```

##### 어노테이션 설정

```java
@LogExecution("사용자 정보 조회")
public UserDto getUserInfo() { }

@LogExecution("주문 처리")
public OrderDto processOrder() { }
```

##### 로깅 시스템 구성

1. **MethodExecutionLoggingAspect**: 메서드 실행 로깅 (AOP)
2. **HttpLoggingFilter**: HTTP 요청/응답 로깅 (필터)
3. **logback-spring.xml**: 로그 파일 관리 및 설정

**중요**: `@LogExecution` 어노테이션이 없으면 메서드 실행 로그가 출력되지 않습니다

### 9. MapStruct

**매퍼 인터페이스**

```java
@Mapper(componentModel = "spring")
public interface MemberMapper {
    MemberDto toDto(Member member);
    Member toEntity(MemberDto dto);
}
```

**사용 예시**

```java
// Entity → DTO
MemberDto dto = memberMapper.toDto(member);

// DTO → Entity
Member entity = memberMapper.toEntity(dto);
```

**자동 생성**: 컴파일 시 `MemberMapperImpl` 구현체 생성

### 10. BaseEntity 상속

**모든 엔티티는 `BaseEntity` 상속을 원칙으로 합니다.**

#### 자동 생성 필드

- **OID**: `@PrePersist`에서 자동 생성 (고유 식별자)
- **생성일시**: `@CreatedDate`로 자동 설정
- **수정일시**: `@LastModifiedDate`로 자동 갱신
- **생성자/수정자 OID**: 사용자 추적용

#### 사용 예시

```java
@Entity
@Table(name = "st_members")
public class Member extends BaseEntity {
    // OID, 생성일시, 수정일시 등이 자동으로 관리됨
    private String userId;
    private String name;
}
```

### 11. DTO 네이밍 규칙

#### 기본 원칙

- **API 계층**: `Dto` 접미사 유지
- **Service 계층**: 역할에 따라 `Dto` 접미사 결정

#### 사용 예시

```java
// API 계층 (Dto 유지)
CreateMemberRequestDto, GetMemberResponseDto

// Service 계층 - 비즈니스 핵심 (Dto 유지)
MemberDto

// Service 계층 - 내부 전달 (Dto 제거)
MemberSearchCondition
```

#### 핵심 규칙

- **API 노출**: `Dto` 유지
- **비즈니스 핵심**: `Dto` 유지
- **내부 전달**: `Dto` 제거

### 12. 데이터베이스 스키마

**데이터베이스 테이블 구조는 `ddl/schema_entity.sql`에 정의되어 있습니다.**

#### 스키마 파일

- **위치**: `ddl/schema_entity.sql`
- **내용**: 모든 엔티티 테이블의 CREATE TABLE DDL 스크립트

#### 초기화 스크립트

- **위치**: `src/main/resources/schema_initial.sql`
- **내용**: 서버 부팅 시 자동 실행되는 초기화 스크립트 (예: shedlock 테이블 등)

#### 사용 방법

- **자동 생성**: 애플리케이션 시작 시 `schema_entity.sql`로 엔티티 테이블 자동 생성
- **초기화**: 서버 부팅 시 `schema_initial.sql`로 시스템 테이블 자동 생성
- **설정**: `spring.jpa.hibernate.ddl-auto=none`으로 Hibernate 자동 스키마 생성 비활성화
