> For the complete documentation index, see [llms.txt](https://tech.x2bee.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tech.x2bee.com/dev-guide/developer-guide-en/pjt-prepare/publish-your-docs/api-development-guide.md).

# API Development Guide

The x2bee-api project is a project that provides APIs for each microservice.

It's divided into projects for each microservice, such as x2bee-api-display (Display), x2bee-api-order (Order), and so on.

All APIs in x2bee-api are provided as REST APIs, and input/output values are handled in JSON format.

x2bee-api has no state information such as session information.\
Information needed for API processing must be received as input values each time or obtained via DB lookups, and caching may be used when unavoidable.

x2bee-api can be called from a client, or called from another x2bee-api.

***

## Package Names

Classify and name packages based on the major task category.

e.g.) Sample package/folder

| Category                        | Package Name/Folder Name                                                                   |
| ------------------------------- | ------------------------------------------------------------------------------------------ |
| Controller                      | com.x2bee.api.display.app.controller.sample                                                |
| Service                         | com.x2bee.api.display.app.service.sample                                                   |
| Repository                      | com.x2bee.api.display.app.repository.sample                                                |
| DTO                             | com.x2bee.api.display.app.dto.request.sample com.x2bee.api.display.app.dto.response.sample |
| mapper XML                      | mapper/rwdb/sample mapper/rodb/display                                                     |
| message (multilingual handling) | message/display                                                                            |

{% hint style="info" %}
entity/enum are not separated by task-specific package; all are written under the entity/enum package.
{% endhint %}

## Writing Controllers

A controller receives input parameters, sets them in a DTO, calls a service method to process the business logic, and then converts the service method's result into an appropriate response format to return.

### Class Annotations

Use the following annotations at the class level.

<table><thead><tr><th width="137.888916015625">Type</th><th width="232.7777099609375">Annotation</th><th>Description</th></tr></thead><tbody><tr><td>Spring</td><td>@RestController</td><td>A Spring bean annotation indicating a Rest controller. Serves the role of @ResponseBody + @Controller.</td></tr><tr><td></td><td>@RequestMapping</td><td>Specifies the common part of the Request Mapping URI at the controller class level.</td></tr><tr><td></td><td>@RequiredArgsConstructor</td><td>A lombok annotation for convenient constructor injection.</td></tr><tr><td></td><td>@Slf4j</td><td>A lombok annotation used for writing logs.</td></tr><tr><td>Swagger3 UI</td><td>@Tag</td><td>Used when configuring the Swagger API group. Lets you add a tag name and description.</td></tr></tbody></table>

Example:

```java
@RestController
@RequestMapping("/categories")
@Slf4j
@RequiredArgsConstructor
@Tag(name = "카테고리 관리 Controller", description = "카테고리 API")
public class CategoryController { ... }
```

### Method Annotations

Use the following annotations at the method level.

<table><thead><tr><th width="157.888916015625">Type</th><th width="248.3333740234375">Annotation</th><th>Description</th></tr></thead><tbody><tr><td>HTTP Request</td><td>@GetMapping</td><td>Used for the get method (read/search)</td></tr><tr><td></td><td>@PostMapping</td><td>Used for the post method (create)</td></tr><tr><td></td><td>@PutMapping</td><td>Used for the put method (full update)</td></tr><tr><td></td><td>@PatchMapping</td><td>Used for the patch method (partial update)</td></tr><tr><td></td><td>@DeleteMapping</td><td>Used for the delete method (delete)</td></tr><tr><td>Swagger3 UI</td><td>@Operation</td><td>Used to configure the Swagger API description</td></tr><tr><td></td><td>@ApiResponse</td><td>Used to configure the Swagger API response</td></tr><tr><td></td><td>@Parameters / @Parameter</td><td>Used to configure Swagger API parameters</td></tr></tbody></table>

Example:

```java
@Operation(summary = "카테고리 목록 조회", description = "해당 카테고리 목록을 조회한다")
@Parameters({
  @Parameter(name = "siteNo", description = "사이트번호 (x2bee.com : 1)", required = true, example = "1"),
  @Parameter(name = "useYn", description = "사용여부 (사용함: Y, 사용안함: N)", required = true, example = "Y")
})
@ApiResponses(value = {
  @ApiResponse(responseCode = "200", description = "몰 정보 조회 성공", content = @Content(schema = @Schema(implementation = Category.class))),
  @ApiResponse(responseCode = "400", description = "몰 정보 조회 실패", content = @Content(schema = @Schema(implementation = ErrorCode.class)))
})
@GetMapping(value="/trees")
public List<Category> getCategoryTreeList(PrDispCtgBaseRequest prDispCtgBaseRequest) throws Exception {
    ...
    return categoryTreeList;
}
```

### Mapping URI Format

The format of the mapping URI written as the path parameter of @RequestMapping is as follows.

```
/api/<major-task-category-name(package-name)>/resource-name-plural/sub-resource-name-plural
```

* /api/\<major-task-category-name(package-name)>: Specified as the context-path. Not specified in the program.
* /resource-name-plural: Specified at the class-level @RequestMapping
* /sub-resource-name-plural: Specified at the method-level Mapping. Omitted if not present.

e.g.) Category handling:

* Class-level RequestMapping: @RequestMapping("/api/display/categories")

Key mapping examples:

<table><thead><tr><th width="235">Function</th><th>Method-level RequestMapping</th></tr></thead><tbody><tr><td>Category tree lookup</td><td>@GetMapping("/trees")</td></tr><tr><td>Category detail lookup</td><td>@GetMapping("/{id}")</td></tr><tr><td>Category registration</td><td>@PostMapping("")</td></tr><tr><td>Category update</td><td>@PutMapping("/{id}")</td></tr><tr><td>Category deletion</td><td>@DeleteMapping("/{id}")</td></tr><tr><td>Category unpublish processing</td><td>@PatchMapping("/{id}?displayYn=false")</td></tr></tbody></table>

### Method Parameter Annotations

The following annotations can be used on method parameters.

<table><thead><tr><th width="180">Annotation</th><th>Description</th></tr></thead><tbody><tr><td>@RequestBody</td><td>All API parameters are passed using JSON-type data in the Request body. @RequestBody is used to receive this parameter.</td></tr><tr><td>@Valid, @Validated</td><td>Validates parameter model class member variables. Constraints such as @NotNull, @Size, @Min, @Max, @Digits, @Pattern, etc. can be applied to member variables, and a MethodArgumentNotValidException is thrown on validation failure.</td></tr></tbody></table>

Example:

{% code lineNumbers="true" expandable="true" %}

```java
public Response<String> savePrDispGoodsInfo(@RequestBody @Valid 
PrDispGoodsInfo prDispGoodsInfo) throws Exception {
 ... 
 }
```

{% endcode %}

### Method Return Values

The API response wraps the response data in a Response object and returns it.\
Since @RestController is used at the class level, the actual response value is the Java object converted to JSON.

Example:

{% code lineNumbers="true" %}

```java
@GetMapping(value = "/CtpNames")
public Response<List<String>> getCtpNmList() throws Exception {
    return new Response(zipNoService.getCtpNmList());
}
```

{% endcode %}

The Response class can specify the Timestamp when the response was created and the processing error code/message, and the payload contains the actual response data.

## Writing Service Classes

Service classes handle the core logic of a particular task.

### Interface/Implementation Class Separation

Write one service class per sub-menu, distinguishing between the interface and the implementation class.

e.g.) CategoryService / CategoryServiceImpl

### Service Annotations

Use the following annotations at the class level.

<table><thead><tr><th width="265">Annotation</th><th>Description</th></tr></thead><tbody><tr><td>@Service</td><td>A Spring bean annotation indicating a service class</td></tr><tr><td>@Slf4j</td><td>A lombok annotation for writing logs</td></tr><tr><td>@RequiredArgsConstructor</td><td>A lombok annotation for convenient constructor injection</td></tr></tbody></table>

Example:

{% code lineNumbers="true" %}

```java
@Service
@Slf4j
@RequiredArgsConstructor
public class CategoryServiceImpl implements CategoryService { ... }

public interface CategoryService { ... }
```

{% endcode %}

### Method Composition

A service method performs tasks such as calling repository methods, calling other service APIs, and handling other business logic, then returns the result. Compose it so that the core business logic is implemented.

### Transaction Handling

Explicitly manage registration/update/delete service methods using the **@Transactional** annotation.

* Classes and methods where @Transactional is declared connect to the ReadWrite database.
* Service methods where it is not declared connect to the ReadOnly database.
* When CRUD operations are mixed, explicitly declare the transaction.

e.g.:

{% code fullWidth="true" expandable="true" %}

```java
@Transactional(propagation = Propagation.REQUIRED, readOnly = false, value="orderRwdbTxManager")
```

{% endcode %}

For value, you must specify a transaction manager such as displayRwdbTxManager, orderRwdbTxManager, eventRwdbTxManager, etc.

Be careful to avoid typos so that transactions work correctly.

## Writing Mappers

### Writing the Mapper Interface

Write one \*\*\*Mapper and one \*\*\*TrxMapper per DB table.

* \*\*\*Mapper: select statements (read-only)
* \*\*\*TrxMapper: insert/update/delete statements (read-write)

Writing insert/update/delete statements in a readonly mapper causes an error, so be sure to separate them.

A Mapper is written as two files: an interface Java file and a SQL mapper XML file. The interface records the method signatures to be called, and the SQL is described in the mapper XML.

e.g.: CategoryMapper.java

{% code lineNumbers="true" %}

```java
public interface CategoryMapper {
    public List<CategoryResponse> selectAllCategories();
    public Optional<CategoryResponse> selectCategoryById(Long id);
    public List<CategoryResponse> selectCategories(CategoryRequest request);
}
```

{% endcode %}

### Writing the Mapper XML

Write the SQL to be executed when a Mapper method is called.

e.g.: CategoryMapper.xml

{% code lineNumbers="true" %}

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
   "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.x2bee.api.app.repository.category.CategoryMapper">
  ...
  <!-- 전체 샘플 조회 -->
  <select id="selectAllSamples" resultType="SampleResponse">
    /* SampleMapper.getStStdCd */
    <include refid="sampleList" />
  </select>

  <!-- 샘플 단건 조회 -->
  <select id="selectSampleById" parameterType="long" resultType="SampleResponse">
    /* SampleMapper.selectSampleById */
    select * from (
      <include refid="sampleList" />
    ) a where id = #{id}
  </select>
  ...
</mapper>
```

{% endcode %}

## Writing DTO / Entity

### Writing Request DTO

A Request DTO is a bean object that holds the parameters of a lookup/search controller method.

* When writing a method that has lookup/search condition parameters, you must write a Request DTO to receive the request parameter values.
* DTO classes are created by extending the BaseCommonEntity class. (Includes information related to creator/modifier/creation date-time/modification date-time/paging/Excel download.)
* Use the @Alias annotation on DTO classes so they can be used as a type shorthand in the Mapper.
* Information received in the controller can be used in service/Mapper methods without separate modification.
* serialVersionUID must always be generated as a UUID.

e.g.: PrDispGrpBaseRequest.java

{% code lineNumbers="true" %}

```java
@Alias("PrDispGrpBaseRequest")
@Getter 
@Setter
public class PrDispGrpBaseRequest extends BaseCommonEntity {
    private static final long serialVersionUID = 5756700830219562201L;
    @Schema(description = "그룹코드")
    private String dispGrpTypCd;
    @Schema(description = "그룹번호")
    private String dispGrpNo;
    ...
}
```

{% endcode %}

### Writing Response DTO

A Response DTO is a bean object that holds the result value of a lookup/search controller method.

* When writing a method that has lookup/search results, write a Response DTO.
* DTO classes do not extend BaseCommonEntity.
* Use the @Alias annotation so it can be used as the return type of a Mapper method.
* serialVersionUID must always be generated as a UUID.

e.g.: PrDispGrpBaseResponse.java

{% code lineNumbers="true" %}

```java
@Alias("PrDispGrpBaseResponse")
@Getter 
@Setter
public class PrDispGrpBaseResponse {
    private static final long serialVersionUID = 5756700830219562201L;
    @Schema(description = "그룹코드")
    private String dispGrpTypCd = ConstCode.DISP_GRP_TYP_CD_REP_MKDP;
    @Schema(description = "그룹번호")
    private String dispGrpNo;
    ...
}
```

{% endcode %}

### Writing Entity

An Entity class is a bean object that holds the parameters of a registration/update/delete controller method.

* When writing registration/update/delete methods, receive the request parameter values through an Entity class.
* An Entity class's fields are composed to match the DB table fields exactly.
* Entity classes are created by extending the BaseCommonEntity class.
* Use the @Alias annotation so it can be used in the Mapper XML.
* serialVersionUID must always be generated as a UUID.

e.g.: PrDispCtgBase.java

{% code lineNumbers="true" %}

```java
@Alias("prDispCtgBase")
@Getter 
@Setter
public class PrDispCtgBase extends BaseCommonEntity {
    private static final long serialVersionUID = 5756700830219562201L;
    @Schema(description = "카테고리코")
    private String dispCtgNo;
    @Schema(description = "카테고리명")
    private String dispCtgNm;
    ...
}
```

{% endcode %}

### Error Handling

When an error occurs, throw ApiException(). Specify ApiError as the constructor parameter.

ApiError is an enumeration consisting of error constants that include the error type and message key.

e.g.:

{% code lineNumbers="true" %}

```java
@GetMapping("/error")
public Response<String> getError() {
    if (true) {
        throw new ApiException(ApiError.UNKNOWN);
    }
    return new Response<String>();
}
```

{% endcode %}

Example response returned when calling this example: HTTP status: 400 BAD REQUEST

```json
{
  "timestamp": "2021-09-13T17:30:42.132",
  "code": "9000",
  "message": "알 수 없는 오류입니다.",
  "payload": null
}
```

ApiError example:

{% code lineNumbers="true" %}

```java
@Getter
@AllArgsConstructor
public enum ApiError implements AppError {
    // success
    SUCCESS("0000", "common.message.success", "common.message.success", false),
    // app error
    EMPTY_PARAMETER("1001", "common.error.emptyParameter", "common.error.emptyParameter", false),
    INVALID_PARAMETER("1002", "common.error.invalidParameter", "common.error.invalidParameter", false),
    DATA_NOT_FOUND("1003", "common.error.dataNotFound", "common.error.dataNotFound", false),
    DUPLICATE_DATA("1004", "common.error.duplicateData", "common.error.duplicateData", false),
    INVALID_FILE("1005", "common.error.invalidFile", "common.error.invalidFile", false),
    UPLOAD_FAIL("1100", "common.error.uploadFail", "common.error.uploadFail", false),
    MEMBER_API_FAIL("1200", "common.error.memberApi", "common.error.memberApi", false),
    // unknown error
    UNKNOWN("9000", COMMON_ERROR_UNKNOWN_MSG_CD, COMMON_ERROR_UNKNOWN_MSG_CD, false),
    // ValidationException error
    VALIDATION_EXCEPTION("9100", COMMON_ERROR_UNKNOWN_MSG_CD, COMMON_ERROR_UNKNOWN_MSG_CD, false);

    private final String code;
    private final String messageKey;
    private final String boMessageKey;
    private final boolean isProcess;

    @Override
    public boolean getIsProcess() {
        return isProcess;
    }
}
```

{% endcode %}

common.properties for messages (example)

* common.error.emptyParameter = 파라미터 값이 없습니다: {0}
* common.error.invalidParameter = 파라미터가 올바르지 않습니다.
* common.error.dataNotFound = 데이터가 존재하지 않습니다.
* common.error.bindingError = 파라미터 바인딩 오류입니다.
* common.error.bindingErrorNotNull = 파라미터 바인딩 오류입니다: not null
* common.error.unknown = 알 수 없는 오류입니다.
* common.message.success = 성공

## Properties

* application.yml: Manages Spring configuration values or base class configuration values.
* config/application-\<profile-name>.properties: Manages property items needed for task development. Each task developer can add items as needed.

Example of using a property in Java:

```java
@Value("${app.apiUrl.system}")
private String systemApiUrl;
```

Example of lookup via the environment bean:

```java
String uploadDomain = environment.get("domain.baseUrl");
```

## Using Messages

Messages are managed in the src/main/resources/message folder. Create a separate folder for each major task category, and manage message files per controller.

### Using Messages in Java

Use the MessageResolver class to look up messages. You can look up by message key or use an AppError enum. Messages support multiple languages; the language can be determined by the locale stored in LocalContext, or a Locale can be specified directly when calling the method.

Example of the MessageResolver method signatures:

{% code lineNumbers="true" %}

```
public static String getMessage(String messageKey);
public static String getMessage(String messageKey, Object[] args);
public static String getMessage(String messageKey, Locale locale);
public static String getMessage(String messageKey, Object[] args, Locale locale);

String getMessage(AppError appError);
public static String getMessage(AppError appError, Object[] args);
public static String getMessage(AppError appError, Locale locale);
public static String getMessage(AppError appError, Object[] args, Locale locale);
public static String getMessage(AppError appError, Object[] args, String defaultMessage);
public static String getMessage(AppError appError, Object[] args, String defaultMessage, Locale locale);
```

{% endcode %}

### Logging

* Logging library: uses logback (configuration: logback-spring.xml). Uses the slf4j interface.
* Writing logs: use the @Slf4j lombok annotation, then write using the log object.

e.g.:

```
log.debug("로그테스트 합니다: {}", obj1);
log.info("로그테스트 합니다: {}", obj1);
log.warn("로그테스트 합니다: {}", obj1);
log.error("로그테스트 합니다: {}", obj1);
```

* Log level usage: error (for error situations), debug (for debugging)
* Viewing logs: in the local development environment, view via console or file.\
  Default log file path: c:/X2BEE-DEV/log/x2bee-\*\*\*.log\
  In the dev/production environments, logs can be viewed via the configured Kibana (check access information separately).

## Masking

### How It Works

By applying the @Masking field annotation to a Request DTO and specifying a type, masking of the specified type is automatically applied to the @Masking-applied DTO field during SQL lookups.

e.g.: MaskingCUD.java

{% code lineNumbers="true" %}

```java
@Alias("MaskingCUD")
@Getter @Setter @ToString
public class MaskingCUD {
    @MaskString(type = MaskingType.NAME_KR)
    private String userNmKr; // 성명_한글

    @MaskString(type = MaskingType.NAME_EN)
    private String userNmEn; // 성명_영문

    @MaskString(type = MaskingType.BIRTH)
    private String userBirth; // 생년월일

    @MaskString(type = MaskingType.RRN)
    private String userRrn; // 주민번호

    @MaskString(type = MaskingType.PHONE_NUM)
    private String userPhoneNum; // 전화번호

    @MaskString(type = MaskingType.MOBILE_NUM)
    private String userMobileNum; // 핸드폰

    @MaskString(type = MaskingType.ADDRESS)
    private String userAddress; // 주소

    @MaskString(type = MaskingType.ADDRESS_DTL)
    private String userAddressDtl; // 상세주소

    @MaskString(type = MaskingType.IP)
    private String userIP; // IP

    @MaskString(type = MaskingType.EMAIL)
    private String userEmail; // 이메일

    @MaskString(type = MaskingType.ID)
    private String userID; // ID

    @MaskString(type = MaskingType.ACTN)
    private String userActn; // 계좌번호

    @MaskString(type = MaskingType.CARD)
    private String userCard; // 카드번호
}
```

{% endcode %}

### Masking Types

<table><thead><tr><th width="143">Masking Type</th><th width="148" align="right">Type Code</th><th>Minimum Requirement</th><th>Description</th></tr></thead><tbody><tr><td>Name</td><td align="right">NAME_KR</td><td>Mask the second character of the name</td><td>홍<em>동 / 을</em>문덕</td></tr><tr><td>English Name</td><td align="right">NAME_EN</td><td>Mask all but the first and last letters of the name</td><td>John Smith → J**h Smith</td></tr><tr><td>Phone Number</td><td align="right">MOBILE_NUM</td><td>Mask the entire middle number</td><td>010****2134</td></tr><tr><td>Address (dong level and below)</td><td align="right">ADDRESS</td><td>Mask the entire address at the dong level and below</td><td>서울 강남구 압구정동 ****</td></tr><tr><td>Road name (gil level and below)</td><td align="right">ADDRESS</td><td>Mask the entire address at the gil level and below</td><td>서울 강남구 압구정로 ****</td></tr><tr><td>Detailed Address</td><td align="right">ADDRESS_DTL</td><td>Mask the entire detailed address</td><td>-</td></tr><tr><td>Email</td><td align="right">EMAIL</td><td>Mask from the 4th character of the ID to the end</td><td>abc***@**********</td></tr><tr><td>Resident Registration Number</td><td align="right">RRN</td><td>Mask 7 or more digits at the end of the resident registration number</td><td>801212-*******</td></tr><tr><td>Date of Birth</td><td align="right">BIRTH</td><td>Mask the day digits</td><td>1980-12-**</td></tr><tr><td>Driver's License Number</td><td align="right">LICENSE</td><td>Mask 6 or more digits starting from the 5th character</td><td>서울 95-******-61</td></tr><tr><td>Passport Number</td><td align="right">PASSPORT</td><td>Mask 4 or more digits at the end</td><td>M9999****</td></tr><tr><td>Cash Receipt Card</td><td align="right">CARD</td><td>Mask 4 or more digits starting from the 9th character</td><td>1544-2020-****-123456</td></tr><tr><td>Credit Card (14 digits)</td><td align="right">CARD</td><td>Mask 4 or more digits starting from the 8th character</td><td>9500-0012-****-0000</td></tr><tr><td>Other Card (11 digits)</td><td align="right">CARD</td><td>Mask 4 or more digits starting from the 7th character</td><td>9500-00**-**0</td></tr><tr><td>Other Card (13–19 digits)</td><td align="right">CARD</td><td>Mask 4 or more digits starting from the 8th character</td><td>9500-0012-****-0000</td></tr><tr><td>Business Registration Number</td><td align="right">BNO</td><td>Mask 4 or more digits starting from the 3rd character</td><td>12*-**-*1234</td></tr><tr><td>Account Number</td><td align="right">ACTN</td><td>Mask from the 6th character to the end</td><td>12345**********</td></tr><tr><td>QR Code</td><td align="right">QRCODE</td><td>Mask 4 or more digits starting from the 5th character</td><td>126-0-****-1234</td></tr><tr><td>IP</td><td align="right">IP</td><td>Mask 3 or more digits starting from the 7th character</td><td>123.123.***.123</td></tr><tr><td>ID</td><td align="right">ID</td><td>Mask from the 4th character to the end</td><td>kim******</td></tr></tbody></table>

### Using MaskingUtils

If individual data masking is needed, you can use MaskingUtils.

| Method                                        | Description                                           |
| --------------------------------------------- | ----------------------------------------------------- |
| masking(String src, int startIdx)             | Masks from startIdx to the end of the string          |
| masking(String src, int startIdx, int length) | Masks for a length of "length" starting from startIdx |

## Encryption/Decryption of DB Data Fields

### How It Works

Apply the @Encrypt field annotation to fields to be encrypted in Request DTOs and Entities.\
When the Mapper performs a SQL insert/update, the @Encrypt-applied field is encrypted and stored in the DB; when a select is performed, it is decrypted and retrieved.

e.g.:

<pre class="language-java" data-line-numbers><code class="lang-java">@Alias("EncryptCUD")
@Getter 
@Setter
<strong>public class EncryptCUD {
</strong><strong>    @Encrypt
</strong><strong>    private String userNmKr; // 성명_한글 암복호화
</strong>
<strong>    @Encrypt
</strong><strong>    private String userNmEn; // 성명_영문 암복호화
</strong><strong>}
</strong></code></pre>

### Applied Algorithm

AES-256-GCM is used for encryption/decryption. Summary of the applied algorithm:

<table><thead><tr><th width="270">Item</th><th>Configuration</th></tr></thead><tbody><tr><td>Encryption Algorithm</td><td>AES-256-GCM</td></tr><tr><td>Data Encryption Key Length (bits)</td><td>256</td></tr><tr><td>Key Derivation Algorithm</td><td>HKDF (including SHA-384)</td></tr><tr><td>Signature Algorithm</td><td>ECDSA including P-384 and SHA-384</td></tr><tr><td>Term Agreement</td><td>HKDF (including SHA-512)</td></tr></tbody></table>

### Ciphertext Data Length

Encrypted data is longer than the plaintext, and this must be taken into account when designing DB fields.

Formula for calculating the encrypted field length

```
Encrypted field length = 880 + (original field length * 4/3)
```

### Using EncryptUtils

If individual encryption/decryption is needed, use the static methods of EncryptUtils.

* Encryption: public static String getEncryptValue(String value) throws Exception;
* Decryption: public static String getDecryptValue(String value) throws Exception;

### Encryption Key Environment Variable

The key used for encryption/decryption is managed in application.yml. The encryption key must be 32 characters.

e.g.:

{% code lineNumbers="true" %}

```yaml
crypto:
  secret:
    key: X2BEE_Application_DATA_SecretKey
```

{% endcode %}

## Method Permissions and Logged-in User Information

Example:

```java
@Secured("ROLE_MEMBER")
@GetMapping("/{id}/secure")
public ResponseEntity<Response<List<SampleResponse>>> getSampleUser(
    @AuthenticationPrincipal UserDetail userDetail,
    @RequestBody SampleRequest sampleRequest) throws Exception {

    if (!userDetail.getMbrNo().equals(sampleRequest.getMbrNo())) {
        AppException.exception(ApiError.NOT_AUTHORIZED);
    }
    return restApiService.get(getUrl("/search"), sampleRequest);
}
```

* Using @Secured("ROLE\_MEMBER") allows access only for logged-in members.
* If not logged in, calling it results in an HTTP 403 FORBIDDEN.
* In a controller method, the logged-in user's information is injected and used via @AuthenticationPrincipal.
* You can look up the member number via userDetail.getMbrNo().

## Swagger3 @Schema

<table><thead><tr><th width="143">Annotation</th><th width="203">Attribute</th><th>Description</th></tr></thead><tbody><tr><td>@Schema</td><td>description</td><td>Korean name</td></tr><tr><td></td><td>defaultValue</td><td>Default value</td></tr><tr><td></td><td>allowableValues</td><td>Allowed values (enumeration)</td></tr><tr><td></td><td>example</td><td>Example value</td></tr></tbody></table>

When writing Request/Response DTOs, set the above annotations and attributes so they can be viewed in Swagger UI.

***
