캐모마일 개발 표준 가이드
개요
목적
- 본 문서는 캐모마일3.0 고도화 프로젝트 개발을 위한 표준을 제시하는데 목적이 있습니다.
범위
- 본 문서는 목표 시스템 구축을 위한 신규 개발되는 시스템을 범위로 하며 솔루션 및 라이브러리는 대상에서 제외됩니다.
Naming Rule 가이드
Naming 표기법
기본 Naming Rule
- 이해가 가능한 Full English Description 방식을 사용합니다.
- 독립어나 두 단어가 조합될 경우 두 번째 명사의 시작 문자는 대문자로 시작합니다.
- 이미 잘 알려진 이니셜 단어는 대문자로 구성합니다.
- 정의되는 이름의 길이는 최대 30자 이하로 사용합니다 ( e.g. 클래스, 인터페이스, 메서드, 변수, 상수, 파일 명)
- 예약어를 사용하지 않는다.
- 상수는 영문 대문자 스네이크 표기법을 사용한다. ex) CONTENT_MANAGEMENT
- 변수, 함수에는 카멜 표기법을 사용한다.
- URL, HTML 같은 범용적인 대문자 약어는 대문자 그대로 사용한다
Java Programming Naming Rule
Package
- Package 명은 반드시 소문자만 사용합니다.
- Package를 정의할 때는 레벨화하여 명명함을 기본으로 합니다.
- 각 레벨에 올 수 있는 단어는 2~15자 내외의 영문 소문자를 사용합니다.
Package 레벨 구조
Level | 예시 | 설명 |
---|---|---|
1 | api | Controller 클래스 Swagger 인터페이스 |
2 | dto | requestVO 관리 |
3 | domain | 업무 별 VO(Value Object) 관리 |
4 | service | Service 클래스들을 관리 |
5 | config | 어플리케이션의 설정 파일들을 관리 |
6 | exception | Exception 공통 처리 및 업무 별 사용되는 공통 Exception 클래스 들을 관리 |
- Class 이름은 간단하고 명시적으로 작성합니다.
- Class 이름은 각 단어를 대문자로 표기하는 PascalCase로 작성합니다.
- 단어 사이의 구분을 위한 밑줄("_")을 사용하지 않습니다.
- Controller class: "서비스 명 + Controller.java"형태로 사용합니다. (e.g. RestController.java)
- Exception class: "명사 + Exception.java" 형태로 사용합니다. (e.g. RuntimeException.java)
Method
- 일반적으로 Method는 클래스의 여러 가지 행위를 나타내는 것이기 때문에 첫 단어는 동사로 시작합니다.
- 동사만으로 의미 전달이 불명확한 경우에는 동사 + 명사 형태로 표기합니다.
- CamelCase로 작성합니다.
Method 명칭 | 설명 |
---|---|
getMethod | – 특정 상세 조회 – 특정 Entity / Field의 값을 설정 |
getTotalCount | 목록 조회의 총 건수 |
setMethod | 특정 Entity / Field의 값을 설정 |
selectMethod | 목록 조회 |
insertMethod | 데이터 등록 (Insert Transaction) |
updateMethod | 데이터 업데이트 (Update Transaction) |
deleteMethod | 데이터 삭제 (Delete Transaction) |
isMethod | 특정 속성 여부 검사 |
hasMethod | 특정 속성 소유 검사 |
변수
- 변수 이름을 정의할 때는 약어 사용은 자제하며, 되도록 변수명만 보고 의미를 알 수 있도록 단어 그대로 사용하는 것을 권장합니다.
- CamelCase로 작성합니다.
- 첫 글자는 밑줄(
_
)이나 달러 문자로 ($
) 시작하지 않습니다.
상수 Constant
- 상수 선언은 반드시 "static final"을 사용합니다.
- 상수는 전부 대문자 스네이크 표기법으로 표기합니다.
- 단어와 단어 사이는 밑줄로 (
_
) 연결합니다. - 첫 글자는 밑줄(
_
)이나 달러 문자로 ($
) 시작하지 않습니다. - 누구나 이해할 수 있는 영문 이름을 사용합니다.
Mapping XML
Java의 대표적 ORM Framework 중의 하나인 Apache MyBatis Framework를 사용하는 경우 SQL Mapping 정보가 저장되는 각종 XML 내의 여러 항목들의 이름을 지정할 때 아래와 같은 규칙을 이용해 파일을 생성하도록 합니다.
- Map XML 파일은 기본적으로 Mapper 인터페이스당 하나를 생성하도록 합니“다.
- 파일명은 "Mapper" 접미사를 사용하여 인터페이스와 XML 파일을 동일하게 생성합니다. (예:
MasterSampleMapper.java > MasterSampleMapper.xml
) - 확장자는 "xml"만 사용합니다.
Mapping XML ID Naming Rule
-
Mapping XML은 SQLMap XML 파일 내에서 사용하는
<select>
,<insert>
,<update>
,<delete>
,<parameterMap>
,<resultMap>
,<typeAlias>
등의 XML Tag들을 뜻하며, "ID"는 이들 구문을 구분하기 위한 unique한 값을 뜻합니다. -
기본적으로 Mapping XML ID는 ****Java의 Method 명명 규칙을 이용하여 작성하도록 합니다.
-
Mapping XML ID는 기본적으로 Class의 Method 명과 동일하게 사용합니다.
-
SQLMap XML 파일에서 namespace를 반드시 지정합니다.
-
Namespace는 SQL 문장의 ID 중복을 회피하기 위한 방편이며,
Namespace는 패키지 이름을 사용합니다.- 예: Namespace는
XXXMapper
인터페이스의 풀 패키지를 사용합니다.
- 예: Namespace는
-
ID는 실제로 호출하는 Mapper 인터페이스의 메서드명과 동일하게 작성하는 것이 좋습니다.
- 예:
XXXMapper.java
의YYY()
을 이용해 데이터 처리를 해야 한다면, Mapping XML ID는<select id="YYY">
형태로 작성하는 것을 뜻합니다.
- 예:
JAVA 코딩 가이드
- Java 코딩 가이드는 프로그래밍 시 지켜야 할 형식적인 규칙을 정의하고, Coding Style을 표준화 하여 제공함으로써 향후 효율적인 프로그램 유지 보수와 가독성을 최대화 하고, 모든 개발자가 상호 간 Code를 읽고 이해하기 쉽게 함으로써 그에 따른 비용을 최소화 할 수 있다.
기본 Style
Line Length
- 모든 줄은 120자 이내에서 완성 되어야 한다. 만약 더 필요할 경우, 연산자 앞에서 개행 한 후 다음 줄에서 이어서 작업 한다.
들여쓰기 (Indentation)
-
소스 코드에 대한 가독성 향상을 위해 아래와 같은 들여쓰기를 사용한다.
- 들여쓰기는 Tab 문자가 아닌 공백(Space) 문자를 사용한다.
- 1 Indent = 4 Spaces ( 4 bytes )
변수 선언
- 변수의 선언은 프로그램 시작 부분에 두는 것을 기본으로 하며, 1라인에 1개의 변수 만을 선언 한다.
Block의 구성
-
각 블록 문장의 중괄호 열기“{“ 는 블록이 들어가는 라인의 가장 마지막 문자 위치에,
-
중괄호 닫기 부호 “}“는 별도의 라인의 첫 번째 문자와 위치를 맞춘다.
-
소스 코드는 레벨이 낮아 질 때마다 한 단계 들어가는 계단형 코딩을 한다.
public class GrpcSampleAdapter implements GetGrpcSamplePort, PostGrpcSamplePort {
private SampleGrpc.SampleBlockingStub sampleStub;
@Override
public List selectGrpcSample() {
List grpcSampleList = new ArrayList<>();
Iterator response = this.sampleStub.selectGrpcSample(null);
response.forEachRemaining(sampleResponse -> {
SampleFieldEntry fieldEntry = sampleResponse.getResult();
Sample sample = Sample.builder()
.seq(fieldEntry.getSeq())
.title(fieldEntry.getTitle())
.contents(fieldEntry.getContents())
.txt(fieldEntry.getTxt())
.build();
grpcSampleList.add(sample);
});
return grpcSampleList;
}
}
주석 (Comment)
구현 주석
- 구현 주석은 각각의 구현에 대한 추가적인 설명이 필요할 때, 또는 코드를 주석 처리할 때 사용할 수 있습니다.
Block 주석
- Block 주석은 파일, 메서드, 자료 구조, 알고리즘에 대한 설명을 제공하는 데 사용됩니다.
- Block 주석은 클래스 파일의 맨 처음, 각 메서드 앞단에 사용해야 합니다.
- 메서드 안에서 사용할 경우, 설명하고자 하는 코드와 같은 간격으로 Indentation을 줍니다.
- Block 주석을 사용할 때는 다른 코드와의 구별을 위해 시작하기 전에 공백 행을 하나 띄어줍니다.
/**
* <pre>
* 테스트 관련 컨트롤러.
* </pre>
*
* @author GildongHong
* @since 2023-09-26
* @version 3.0
* @Modification
* <pre>
* since author description
* =========== ============= ===========================
* 2023-09-26 GildongHong 최초 생성
*
* </pre>
* Copyright (C) 2023 by LDCC., All right reserved.
*/
public class Test() {
public void test() {
}
}
짧은 주석(Single-Line )
- 짧은 주석은 소스 코드와 같은 수준으로 들여쓰기를 한 상태에서 한 줄로 표현될 수 있습니다.
- 주석을 한 줄로 쓸 수 없을 경우, Block 주석 포맷을 사용합니다.
if (condition) {
/* Single-Line 주석을 작성합니다. */
...
}
Trailing 주석
- 아주 짧은 주석이 필요한 경우 주석이 설명하는 코드와 같은 라인에 작성합니다.
- 실제 코드와는 구분 가능하도록 충분히 간격을 두어야 합니다.
javaCopy code
if (a == 2) {
return TRUE; /* a 가 2 인 경우 */
} else {
return a; /* a 가 2가 아닌 경우 */
}
End-Of-Line 주석
- 주석 기호
//
는 한 줄 모두를 주석 처리하거나 한 줄의 일부분을 주석 처리해야 할 경우 사용할 수 있습니다. - 어떤 코드의 일부분을 주석 처리하기 위해서 여러 줄에 연속해서 사용하는 것은 허락되나, 긴 내용을 주석에 포함하기 위해서 연속되는 여러 줄에 이 주석을 사용하는 것은 허락되지 않습니다.
javaCopy code
if (foo > 1) {
// 처리를 실행합니다.
...
} else {
return false; // false를 반환합니다.
}
문서화 주석
- 문서화 주석은 Java 소프트웨어에 포함되어 있는 javadoc 툴을 사용하여 문서화 주석을 포함하는 HTML 파일을 자동으로 생성할 수 있습니다. 또한, 소스 코드가 없는 개발자들도 읽고 이해할 수 있도록, 실제 구현된 코드와는 상관이 없는 코드의 명세 사항(specification)을 포함합니다. 문서화 주석은 클래스, 인터페이스, 생성자, 메서드, 필드를 설명하고 작성할 수 있습니다.
- ※ 만일, 클래스, 인터페이스, 변수 또는 메서드에 대한 어떤 정보를 제공하고 싶지만 문서화 주석에 어울리지 않는다고 생각되면 클래스 선언 바로 후에 Block 주석 또는 Single-Line 주석을 사용합니다. (예: 클래스 구현에 대한 세부 사항들은 클래스에 대한 문서화 주석이 아닌 클래스 선언문 다음에 Block 주석을 사용합니다.)
- Java는 문서화 주석을 주석 이후에 처음 나오는 선언문과 연결시키기 때문에 메서드 또는 생성자 구현 안에 위치해서는 안됩니다.
기타
- 코드 내부에 특별한 사항을 기술할 경우 행 단위 주석(//)을 사용하지만, 가급적 사용을 줄입니다.
- 특별한 로직을 구현했거나 실수하기 쉬운 부분에 대한 강조를 하고자 할 경우가 아니라면, 가급적 코드 block 내에서의 주석 사용은 자제합니다.
- 주석이 너무 자주 나오면 오히려 코드의 질이 떨어지게 됩니다. 따라서, 코딩 시에 주석이 필요하다고 느껴지는 부분은 그 부분의 프로그램 코드를 더욱 깔끔하게 만들 방안이 없는지 재검토하는 것이 좋습니다.
문 (Statements)**
"if" Statements**
- 모든 if 문에는 항상 중괄호를 사용합니다. 이렇게 함으로써 코드를 일관성 있고 확장에 쉽게 대응할 수 있기 때문입니다.
javaCopy code
// RIGHT
if (value == 0) {
doSomething();
} else {
doAnother();
}
// WRONG
if (value == 0) doSomething();
// WRONG
if (value == 0)
doSomething();
5.3.2. "for" Statements
for 문은 다음과 같이 작성합니다. for문의 초기 값 또는 증감 값 부분에서 콤마 연산자(comma operator)를 사용해야 할 경우, 세 개 이상의 변수를 사용하는 복잡성은 피합니다.
javaCopy code
for (초기 값; 종료 조건; 증감 값) {
statements;
}
// 향상된 for문
for (변수타입 변수이름 : 배열이름) {
statements;
}
"while" Statements**
- while 문은 다음과 같이 작성합니다.
javaCopy code
while (조건문) {
statements;
}
// 빈 while문은 다음과 같이 작성합니다.
while (조건문);
"do-while" Statements
- do-while 문은 다음과 같이 작성합니다.
javaCopy code
do {
statements;
} while (조건문);
"switch" Statements**
- switch 문은 다음과 같이 작성합니다. 모든 switch 문은 반드시 default case를 포함해야 합니다. default case 내의 break는 중복되지만, 추후 다른 case가 추가되는 경우 에러를 방지할 수 있습니다.
javaCopy code
switch (조건문) {
case A:
statements;
/* 계속 수행 (다음 라인) */
case B:
statements;
break;
case C:
statements;
break;
default:
statements;
break;
}
"try-catch" Statements**
- try-catch 문은 다음과 같이 작성합니다. try-catch 문은 try 블록 수행의 성공/실패 여부와 상관 없이 수행되는 finally를 사용할 수 있습니다.
javaCopy code
try {
statements;
} catch (ExceptionClass e) {
statements;
} finally {
statements;
}
"return" Statements**
- 복잡한 표현식이나 리턴 값을 더 명확하게 표현하기 위한 것이 아니라면, 리턴 값이 있는 return 문에는 괄호를 사용하지 않습니다.
javaCopy code
// WRONG
return(true);
// RIGHT
return true;
// RIGHT
return (s.length() + s.offset);
단순문 (Simple Statements)**
- 각 줄에는 최대 하나의 문(Statements)만 사용하도록 합니다.
javaCopy code
// WRONG
a = b + c; count++;
// RIGHT
a = b + c;
// RIGHT
count++;
복합문 (Compound Statements)**
-
복합문은 중괄호 "{ statements }"에 포함된 여러 개의 statement를 포함한 문장입니다.
-
{}
안에 있는 문장은 복합문 보다 한 번 더 들여쓰기를 해야 합니다. -
여는 중괄호 "{" 는 복합문을 시작하는 줄의 마지막에 위치해야 합니다.
-
닫는 중괄호 "}" 는 새로운 줄에 써야 하고, 복합문의 시작과 끝은 동일한 들여쓰기를 해야 합니다.
javaCopy code
// if-else 또는 for와 같이 어떤 statement든 제어 구조의 일부분이라면 중괄호로 모든 statement를 감싸야 합니다.
if (condition) {
// statements...
} else {
// statements...
}
공백 (White Space)**
빈 줄 (Blank Line)**
-
빈 줄은 논리적으로 연관된 코드를 묶음으로써 코드의 가독성을 향상시킵니다.
-
메서드 사이
-
메서드에서 로컬 변수와 그 메서드의 첫 번째 문장 사이
-
시작 주석문과 package문, 그리고 package문과 import문 사이
빈 칸 (Blank Space)**
-
빈 칸은 다음과 같은 경우에 사용합니다.
- 키워드 다음의 괄호는 space에 의해 분리됩니다. (e.g.
while (true)
) - 메서드 이름과 괄호 사이에는 빈칸을 두지 않습니다. 이것은 키워드와 메서드 호출을 쉽게 구분할 수 있게 해 줍니다.
- 매개변수 리스트에서 콤마 다음에 빈칸을 둡니다.
- 콤마(,)를 제외한 모든 이항 연산은 연산자로부터 한 칸 띄워야 합니다. 단항 연산자인 증가 연산자(
++
)와 감소 연산자(-
)의 경우 피 연산자와 분리해서는 안됩니다.
- 키워드 다음의 괄호는 space에 의해 분리됩니다. (e.g.
javaCopy code
a += c + d;
a = (a + b) / (c + d);
while (d++ == s++) {
n++;
}
printSize("size is " + foo + "\n");
- 한 문장 안에서 표현은 빈칸으로 구별됩니다.
javaCopy code
for (expr1; expr2; expr3)
- 변수 타입을 변환하는 cast의 경우, cast 후 빈 칸을 둡니다.
javaCopy code
myMethod((byte) aNum, (Object) x);
myMethod((int) (cp + 5), ((int) (i + 3)) + 1);
선언 (Declarations)
변수 선언
- 각 변수는 주석 처리를 위해 각 라인별로 선언되어야 합니다. 변수명은 세로로 들여쓰기를 맞추어 쉽게 읽을 수 있도록 하며, 타입에 따른 특별한 선언 순서는 없습니다. 특별한 경우(연산처리와 연관되어 초기화 되는 경우)를 제외하고 로컬 변수는 선언한 곳에서 초기화를 합니다.
javaCopy code
int level; // RIGHT
int level, size; // WRONG
int level, tags[]; // WRONG :: 다른 타입의 변수를 같은 라인에 선언하면 안됩니다.
- ※ 타입과 변수 사이에는 1칸의 공백을 이용합니다. 그러나 탭을 사용하는 것도 허용됩니다.
javaCopy code
int level;
int size;
Object currentEntry;
package 및 import**
- package 문은 소스의 가장 상단에 작성하고, 그 하단에 import문을 표시합니다.
- package 문과 import 문 사이에 한 줄을 띄어줍니다.
- package 명은 소문자로 정의합니다.
javaCopy code
package chmm.application.adapter.in.controller;
import java.util.ArrayList;
import java.util.List;
클래스/인터페이스 선언**
-
클래스와 인터페이스 선언 시 다음과 같은 원칙을 따릅니다.
- 메서드 이름과 그 메서드의 파라미터 리스트의 시작인 괄호
(
사이에는 빈 공간이 없어야 합니다. - 여는 중괄호
{
는 클래스/인터페이스/메서드 선언과 동일한 줄의 끝에 작성합니다. - 닫는 중괄호
}
는 여는 중괄호{
후에 바로 나와야 하는 null 문인 경우를 제외하고는 동일한 들여쓰기를 하는 새로운 줄에서 사용합니다.
- 메서드 이름과 그 메서드의 파라미터 리스트의 시작인 괄호
javaCopy code
class Sample extends Object {
int var1;
int var2;
Sample(int i, intj) {
var1 = i;
var2 = j;
}
}
SQL 작성 가이드**
Comment
- 주석(comment)은 개발자가 작성한 Query의 이해를 돕는 설명이나 디버깅을 위해 작성합니다.
Query Description
-
Query XML의 주석은
<!--
로 시작하여-->
로 끝납니다. Query에 대한 주석은 다음과 같은 정보를 포함합니다:- 설명: 쿼리가 처리하는 업무를 다른 개발자들이 이해할 수 있는 수준으로 간단 명료하게 작성합니다.
- 작성일자: 쿼리를 작성한 일자를 기록합니다. (yyyy.mm.dd)
- 작성자: 해당 쿼리를 작성한 사람의 chmm.com 이메일 계정을 입력합니다.
xmlCopy code
<!--
설명 : 샘플의 목록을 조회하는 쿼리입니다.
작성일자 : 2021.07.21
작성자 : xxxx@chmm.com
-->
<select id="selectSample" resultType="SampleEntity">
...
...
...
</select>
Query Inline Comment
- XML Query 내의 주석은
/*
로 시작하여*/
로 끝납니다. Query 내의 주석은 다른 개발자들이 해당 쿼리를 이해할 수 있도록 comment를 남기기 위해 사용합니다. 실행 쿼리 아이디를 주석으로 남깁니다. 아이디는 확장자를 제외한 xml 파일명 + ‘.’ + Query id를 사용합니다.
xmlCopy code
<select id="selectSample" resultType="SampleEntity">
SELECT /* SampleMapper.selectSample */
seq
, title
FROM master_sample
</select>
- 컬럼이나 쿼리 내에 주석을 사용할 필요가 있는 경우:
xmlCopy code <select id="selectSample" resultType="SampleEntity"> SELECT /* SampleMapper.selectSample */ seq /* MASTER_SAMPLE PK */ , title /* 샘플의 제목 */ FROM master_sample </select>
### 공백(White Space)
- 공백은 작성된 쿼리의 depth를 구분하고 개발자 별 IDE의 Formatter로부터 코드를 일관성 있게 유지하여 가독성을 높이는데 사용합니다. 공백은 Tab(space 4칸)과 Space(space 1칸)으로 구분합니다.
- 들여쓰기 전 **IDE의 tab을 space 4개로 변경하도록 설정합니다**. STS의 tab의 space로 변경 설정을 참고합니다.
### CDATA
- Query 구문에 XML 파싱 시 구문으로 인식할 수 있는 **특수 기호를 사용할 경우 오류가 발생할 수 있습니다. 특수기호를 문자열로 인식할 수 있도록 CDATA(**Character Data)를 사용하여 처리합니다. 문자열 처리 구문은 **<![CDATA[ ... ]]>
**를 사용하며 아래와 같이 처리합니다.
````xml
xmlCopy code
<select id="selectSample" resultType="SampleEntity">
SELECT *
FROM master_sample
WHERE seq ]]> 1
</select>
Query 작성 패턴
-
쿼리 작성 시 개발자 별로 다른 패턴을 통일하기 위해 쿼리의 작성 패턴을 가이드합니다.
-
- 키워드, 예약어는 대문자로 작성합니다.
-
- 컬럼명과 테이블 명은 소문자로 작성합니다.
-
- 쿼리의 첫 줄은 xml 파일명과 쿼리 아이디를 주석으로 포함시킵니다.
-
- 쿼리의 예약어의 끝나는 지점의 라인을 맞춰 컬럼, 테이블 명과 영역이 구분될 수 있도록 작성합니다.
-
- 한 라인에 하나의 컬럼만 작성합니다. 라인을 구분하는 콤마(,)는 라인이 시작하는 시점에 사용합니다.
-
- 쿼리의 파라미터 매핑 시 jdbcType은 사용하지 않습니다.
-
<select id="selectCodeMaster" parameterType="String" resultType="chmm.application.domain.code.CodeMaster">
SELECT /* CommonCodeReaderMapper.selectCodeMaster */
code_id
, code_name
, code_dscr
, code_lngt
, code_patr_code
, hgrn_code_id
, sys_name
, tabl_owner_name
, tabl_name
, crtr_id
, cret_dttm
, amnr_id
, amnd_dttm
, dlt_ysno
FROM eom.t_md_cmm_code_m
WHERE code_id = #{codeId}
</select>
- line 2: 1번, 3번 예약어 대문자 사용 및 첫 번째 줄의 쿼리 아이디 주석 사용
- line 3 ~ 16: 2번, 5번 컬럼명의 소문자 사용 및 한 줄에 하나의 컬럼 조회, 라인을 구분하는 콤마를 라인 시작 지점에 사용
- line 2, 17, 18: 1번 예약어의 대문자 사용 및 4번 예약어의 끝나는 라인을 맞춰 영역에 대한 구분
- line 18: 6번 쿼리 파라미터 매핑 시 jdbcType은 사용하지 않음
#{codeId}
(O),#{codeId, jdbcType=VARCHAR}
(X)
Exception
- 프로그램 개발시 예외 처리에 대한 통일성을 유지하고 서비스 호출한 부분에 상황에 따른 적절한 예외 처리를 위해 공통으로 적용한다.
예외 핸들러 (Exception Handler)
-
- @ExceptionHandler 사용하여 유형별로 예외처리에 대응할 수 있습니다.
-
ControllerExceptionHandler
: @RestController에서 예외가 발생할 때 공통으로 예외를 처리하는 클래스입니다.
-
- @RestControllerAdvice 어노테이션을 사용하여 @RestController에서 발생하는 예외를 처리합니다.
-
- @ExceptionHandler 어노테이션을 통해 예외 유형별로 컨트롤할 수 있습니다.
예외 발생 처리 (throw Exception)
-
예외가 발생하면 API를 호출한 서비스에게 오류가 발생한 상황을 알려주도록 합니다. 개발자가 발생 상황을 명확히 인지하고 개별적으로 처리하는 경우가 아니라면, 호출된 상위 서비스로 예외를 throw하도록 작성합니다.
-
DAO단에서 Exception 처리
- DAO 단에서는 스프링에서 제공하는 SqlMapClientDaoSupport를 상속받아 사용하면 SQLException 대신 스프링이 제공하는 예외 클래스(Runtime Exception인 DataAccessException)로 변환해 주기 때문에 별도의 SQL관련 Exception 로직을 사용하지 않는다.
-
Service단에서 Exception 처리
-
Service단에서 DAO에서 발생한 Exception을 별도의 Exception 처리를 하지 않는다.
-
Controller단으로 throw하여 Exception 처리
-
-
Controller 단에서 입셉션을 처리할 수 있으며, 클라이언트 응답을 보내기 위해서는 처리 후 다시 ChamomileException으로 감싸서 throw를 해야 한다. 이렇게 throw된 ChamomileException은 @RestControllerAdvice에서 처리된다.
-
ChamomileExceptionCode 의 경우 별도의 enum 파일로 관리 된다.
-
throw new ChamomileException(ChamomileExceptionCode.MissingParameter);
공통 Exception Handler의 사용
- @RestControllerAdvice를 사용하면 Spring에서 예외가 발생할 때 자동으로 관리합니다.
- Exception Handler에서 io.ServerInterceptor를 구현한 Interceptor의 클래스를 등록하면, 해당Service에서 예외 발생 시 공통으로 처리가 가능합니다.
로깅 (Logging Guide)
- 프로젝트에서 개발되는 여러 애플리케이션에서 디버깅을 위해 로그를 출력하거나 예외/오류 상황이 발생할 경우, 오류 정보를 출력하여 개발 및 운영 시 문제 상황에 대한 판단과 빠른 조치를 할 수 있도록 로그를 출력하는 방법과 출력된 로그를 관리하는 방법에 대해 가이드합니다.
로깅 (Logging)
-
Spring Boot는 모든 내부 로깅에 Commons Logging을 사용하지만 기본 로그 구현은 오픈되어 있습니다. Java Util Logging, Log4J2 및 Logback에 대한 기본 구성이 제공됩니다. 각각의 경우에 logger는 선택적 파일 출력도 사용 가능한 콘솔 출력을 사용하도록 사전 구성되어 있습니다.
- Log는 반드시 Framework에서 제공한 Logger만을 사용한다.
- Log는 debug, info, warn, error로 구별하여 사용한다.
- Log는 반드시 발생 시간과 위치 그리고 내용을 포함한다.
- Log는 한 줄만 출력한다(debug log 제외).
- debug log는 개발자가 개발 시에만 사용하고, 운영 중에는 사용하지 않는다.
- info log는 운영자에게 도움이 될 내용을 기록한다.
- warn log는 error는 아니나 잠재적인 error의 발생이 가능한 내용을 기록한다.
- error log는 error code와 함께 error에 대한 내용을 기록한다.
- Unchecked Exception이 발생할 경우에는 Stack Trace내용을 로그에 남기도록 한다.
제한 사항 (Restriction)
System.out.println()
은 사용하지 않습니다. 디버그 메시지를 출력하기 위해 종종System.out.println()
을 사용하는 경우, 이러한 방식은 동기화 처리로 인해 성능 문제와 로그 관리 문제를 야기할 수 있습니다.
로그 레벨 (Log Level)
- 로그 레벨은 TRACE, DEBUG, INFO, WARN, ERROR로 구분되며, 각각 상세한 정보부터 에러까지를 표현합니다.
Log Level | 설명 |
---|---|
TRACE | 추적 레벨은 Debug보다 훨씬 상세한 정보를 나타낸다. |
DEBUG | 개발자가 작성한 프로그램을 디버깅 하기 위한 상세 정보를 표시 한다. |
INFO | 프로그램의 상태 정보 (처리 시작, 처리 완료 등…)와 같은 정보성 로그를 표시 한다. |
WARN | 처리 가능한 문제, 향후 시스템 에러의 원인이 될 수 있는 경고성 메시지를 표시 한다. |
ERROR | 요청을 처리하는 중 발생한 오류를 표시 한다. (※ Logback에는 FATAL 레벨이 없고, ERROR에 매핑 된다.) |
로그 우선 순위 (Log Priority)
로그는 레벨에 따라 TRACE < DEBUG < INFO < WARN < ERROR의 우선 순위로 기록되며, 출력 레벨의 설정에 따라 설정 레벨 이상의 로그를 출력합니다.
로그 출력 예시 (Log 출력 Sample)
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class RestSampleController {
@PostMapping(path = "/create-test")
public ChamomileResponse createTest (@Validated @RequestBody TestVO request) throws Exception {
log.debug("test");
return new ChamomileResponse<>();
}
}
SpringBoot
Profile
- 기본 프로파일은 local, dev, prd로 3개를 관리합니다.
- 개발 툴에서 프로파일 설정은 실행 인자로
Dspring.profiles.active=local
형태로 지정합니다.
Config
- 기본 설정데이터는
application.yml
파일에 작성하되, 패스워드증 민감한 데이터의 경우 jasypt 암호화 하여 관리합니다.
프로젝트 구성
- 단위 프로젝트는 항상 chmm-core 라이브러리가 추가 되어 있습니다.
net.lotte.chamomile
chamomile-core
net.lotte.chamomile.boot
chamomile-boot-starter
net.lotte.chamomile.module
chamomile-security-jwt
net.lotte.chamomile.module
chamomile-database
net.lotte.chamomile.module
chamomile-database-audit
net.lotte.chamomile.module
chamomile-web
프레임워크 주요 모듈
카테고리 | 설명 |
---|---|
Core | 예외, 로깅, 라이센스 등 프레임워크에 사용되는 핵심 모듈 |
Test | H2 DB를 사용하여 개발과 분리된 테스트 환경 제공 |
Commoncode | 공통코드 관련 기능 제공 |
Database | Batch, 파일업로드 처리 등 DB 관련 기능 제공 |
Database-audit | DB 작업에 대한 데이터를 모니터링하고 수집하는 기능 제공 |
File | 파일 업로드/다운로드 등 파일 관련 기능 제공 |
File-drm | DRM 파일 관련 기능 제공 |
File-excel | 엑셀 업로드/다운로드/생성 관련 기능 제공 |
ftp | FTP/SFTP 등의 기능 제공 |
I18n | 다국어 처리 관련 모듈 |
Mask | 마스킹 처리 관련 모듈 |
Menu | 트리 구조의 메뉴 데이터를 리스트 형태로 가져오는 기능 제공 |
Notification | SMTP 서버 기반 메일 발송 기능 제공 |
User | DB 사용자 관리를 위한 CRUD 기능 제공 |
Util | String, XML, UUID 등의 변환기능을 제공 |
Web | 웹 어플리케이션과 관련하여 기본적인 필터, 에러 처리, 핸들러 등의 기능 제공 |
Security | Spring Security 기반의 기본 설정, 세션 및 쿠키 기반의 인증 처리 기능 제공 |
Security-jwt | JWT 기반 인증 처리 제공, 인증 구성 커스터마이징 개선 |
cache | hazelcast cache 기능 제공 |
cache-redis | redis cache 기능 제공 |
Adapter-nexcacro | Nexacro 등의 상용 UI 솔루션과 연동할 수 있는 기능 제공 |
Adapter-nexcaro14 | Nexacro 등의 상용 UI 솔루션과 연동할 수 있는 기능 제공 |
Adapter-nexcaro17 | Nexacro 등의 상용 UI 솔루션과 연동할 수 있는 기능 제공 |
Controller 작성 방법
**
* <pre>
* 권한 관련 컨트롤러.
* </pre>
*
* @author teahoPark
* @since 2023-09-26
* @version 3.0
* @Modification
* <pre>
* since author description
* =========== ============= ===========================
* 2023-09-26 TaehoPark 최초 생성
*
* </pre>
* Copyright (C) 2023 by LDCC., All right reserved.
*/
@Slf4j
@RestController
@RequestMapping(path = "/chmm/auth")
@RequiredArgsConstructor
public class AuthController implements AuthControllerDoc {
private final AuthService authService;
private final FileUploader fileUpload;
@GetMapping(path = "/list")
public ChamomileResponse> getAuthList(AuthQuery authQuery, Pageable pageable) {
Page results = authService.getAuthList(authQuery, pageable);
return new ChamomileResponse<>(results);
}
}
Service 클래스 작성 방법**
/**
* <pre>
* 권한 관련 서비스 인터페이스 구현체.
* </pre>
*
* @author TaehoPark
* @since 2023-09-20
* @version 3.0
* @Modification
* <pre>
* since author description
* =========== ============= ===========================
* 2023-09-20 TaehoPark 최초 생성
*
* </pre>
* Copyright (C) 2023 by LDCC., All right reserved.
*/
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {
private final AuthMapper authMapper;
@Override
public Page getAuthList(AuthQuery authQuery, Pageable pageable) {
return authMapper.findAuthListData(authQuery, pageable);
}
}
Mapper 작성 방법
/**
* <pre>
* 권한 관련 마이바티스 매핑 인터페이스.
* </pre>
*
* @author TaehoPark
* @since 2023-09-26
* @version 3.0
* @Modification
* <pre>
* since author description
* =========== ============= ===========================
* 2023-09-26 TaehoPark 최초 생성
*
* </pre>
* Copyright (C) 2023 by LDCC., All right reserved.
*/
@Mapper
public interface AuthMapper {
Page findAuthListData(AuthQuery query, Pageable pageable);
List findAuthListData(AuthQuery query);
}
- 위 내용은 프로젝트 구성과 관련된 Spring Boot 개발 프레임워크 사용 가이드입니다. 필요에 따라 추가적인 내용이나 세부 사항을 더 확인하시려면 해당 프로젝트에서 사용하는 라이브러리의 문서와 가이드를 참고하시면 도움이 될 것입니다
표기법 명칭 | 설명 | 예제 |
---|---|---|
Pascal Case | ||
(파스칼 표기법) | 첫 문자를 대문자로 시작하며, 의미 구분을 위해 대문자를 사용 | ContentManagement |
Camel Case | ||
(카멜 표기법) | 첫 문자를 소문자로 시작하며, 의미 구분을 위해 대문자를 사용 | contentManagement |
Hungarian Case | ||
(헝가리안 표기법) | 사전에 정의된 접두어를 사용 | strContentManagement |
Lower Case | ||
(소문자 표기법) | 전체를 소문자로 사용 | contentmanagement |
Upper Case | ||
(대문자 표기법) | 전체를 대문자로 사용 | CONTENTMANAGEMENT |
Underscored Case | ||
(언더스코어 표기법) | 의미 구분을 위해 언더스코어를 사용 | content_management |
Hyphen Case | ||
(하이픈 표기법) | 의미 구분을 위해 하이픈을 사용 | conent-management |
RestAPI
- API란 Application Programming Interface의 약자로 프로그램을 실행하는 인터페이스입니다. API를 통해 프로그램에 요청을 전달하기 위한 통로 혹은 방법으로 생각할 수 있습니다. 라이브러리를 설치하고 호출하는 함수도 API라고 합니다.
RESTful API의 탄생
- REST는 Representational State Transfer의 약어로서 2000년도에 로이 필딩 (Roy Fielding)의 박사학위 논문에서 최초로 소개되었습니다. 로이 필딩은 HTTP의 주요 저자 중 한 사람으로 그 당시 웹(HTTP) 설계의 우수성에 비해 제대로 사용되어지지 못하는 모습에 안타까워하며 웹의 장점을 최대한 활용할 수 있는 아키텍처로써 REST를 발표했습니다. REST API는 HTTP Method별로 역할을 명시하는 것이 특징입니다.
RESTful API에서 HTTP Method
- GET: 조회 (받겠다)
- POST: 리소스 생성 (보내겠다)
- PUT: 리소스 전체 갱신(놓겠다/넣겠다)
- DELETE: 리소스 삭제 (지정한 서버의 파일을 삭제하겠다)
- 캐모마일 에서는 GET, POST만 사용한다.
REST API 디자인 가이드
- URI는 정보의 자원을 표현해야 한다.
- 자원에 대한 행위는 HTTP Method(GET, POST)로 표현한다.
- **캐모마일에서는 GET, POST 사용**
URL Rules
- 마지막에 / 포함하지 않는다.
- _(underbar) 대신 -(dash)를 사용한다.
- (dash)의 사용도 최소한으로 설계한다. 정확한 의미나 표현을 위해 단어의 결합이 불가피한 경우 반드시 -(dash) 사용한다.
- Bad: http://api.test.com/users/post_commnets
- Good: http://api.test.com/users/post-commnets
- 소문자를 사용한다.
- 행위(method)는 URL에 포함하지 않는다.
- Bad: POST http://api.test.com/users/1/delete-post/1
- Good: DELETE http://api.test.com/users/1/posts/1
- 컨트롤 자원을 의미하는 URL 예외적으로 동사를 허용한다.
- 함수처럼, 컨트롤 리소스를 나타내는 URL은 동작을 포함하는 이름을 짓는다.
- Bad: http://api.test.com/posts/duplicating
- Good: http://api.test.com/posts/duplicate
- 모든 Resource는 유일한 URI를 지녀야 한다.
- URI는 직관적인 단어들을 사용해야 한다.
- /myroom/order
- URI는 hierarchical 하도록 구성해야 한다
- • /myroom/order/detail/1007
- URI의 상위 경로는 하위 경로의 집합을 의미하는 단어로 구성해야 한다
- /category/books/novel/20210803
- URI의 novel 은 책(book) 의 하위 카테고리를 의미 한다.
- Resource와 직접적인 관련이 없는 정보는 Query String으로 처리 한다.
- /category/books/novel?regDate=20210801
- URI 경로에는 소문자를 사용한다.
- URI 는 대소문자에 따라 다른 URI 로 판단하므로 소문자로 통일해서 사용한다.
- 확장자 사용을 하지 않는다
- g. .html, .do, .jsp, .xml, .json …
- URL 인코딩이 필요한 문자를 사용하지 않는다.
- g. spaces..
Content-Type**
- application/json
Use HTTP status**
- 응답 상태 코드만으로도 많은 정보를 전달할 수 있기 때문에 응답의 상태코드 값을 명확히 돌려주는 것은 중요합니다.
의미에 맞는 HTTP status를 리턴
- Bad:status는 200으로 성공인데 body 내용엔 실패에 관한 내용을 리턴하고 있다. 모든 응답을 200으로 처리하고 body 내용으로 성공|실패를 판단하는 구조에서 사용된다. 잘못된 설계다.
HTTP/1.1 200 OK
{
"result" : false
"status" : 400
}
- Good:
HTTP/1.1 400 Bad Request
{
"code" : 400,
"message" : "check your parameter"
"data" : "test"
}
HTTP status만으로 상태 에러
-
세부 에러 사항은 응답 객체에 표시하거나, 해당 에러를 확인할 수 있는 link를 표시한다. HTTP 상태 코드를 응답 객체에 중복으로 표시할 필요 없습니다.
-
Bad:
cssCopy code HTTP/1.1 404 Not Found { "code" : 404, "error_code": -765 }
-
Good:
cssCopy code HTTP/1.1 404 Not Found { "code" : -765, "message" : "https://api.test.com/errors/-765" "data" : "https://api.test.com/errors/-765" }
Resource Path
-
API Service 의 REST 한 URI 이름을 짓기 위해서는 몇가지 원칙이 있다.
-
URI 생성 원칙
- 문서가 없이도 API 의 기능을 직관적으로 알 수 있는 이름을 사용한다.
- 좋은 이름은 좋은 개발을 할 수 있도록 도와준다.
- 이름 생성이 어렵다면 좋지 않은 설계의 징후로 볼 수 있다.
- API를 사용하는 사람 관점에서 생각한다.
- 전체 API 에 걸쳐서 일관성 있게 생성한다.
- 좋은 URI 의 조건
- 발음하기, 검색하기 쉬운 이름
- 문제 지향성 (Problem Orientation)
- ‘어떻게’ 보다 ‘무엇’을 표현
- 역할과 의도 제시
- 의도 제시 형 이름
- 세부 구현에 의존적이지 않게
-
Resource path 생성 규칙은 아래 표 에서 확인 할 수 있다.
Controller Method Naming Convention | Mapping Result |
---|---|
List |
HTTP GET /books |
Book getBook(bookId) | HTTP GET /books/{bookId} |
void insertBook(Book book) | HTTP POST /books |
void updateBook(bookId, Book book) | HTTP PUT /books/{bookId} |
void deleteBook(bookId) | HTTP DELETE /books/{bookId} |
- Resource path 생성 규칙에 의한 Controller Class 생성 예시
/**
* <pre>
* 권한 관련 컨트롤러.
* </pre>
*
* @author test
* @since 2023-09-26
* @version 3.0
* @Modification
* <pre>
* since author description
* =========== ============= ===========================
* 2023-09-26 test 최초 생성
*
* </pre>
* Copyright (C) 2023 by LDCC., All right reserved.
*/
@Slf4j
@RestController
@RequestMapping(path = "/chmm/auth")
@RequiredArgsConstructor
public class AuthController implements AuthControllerDoc {
private final AuthService authService;
private final FileUploader fileUpload;
@GetMapping(path = "/list")
public ChamomileResponse> getAuthList(AuthQuery authQuery, Pageable pageable) {
Page results = authService.getAuthList(authQuery, pageable);
return new ChamomileResponse<>(results);
}
@PostMapping(path = "/create")
public ChamomileResponse createAuth(@Validated @RequestBody AuthVO request) throws Exception {
authService.createAuth(request);
return new ChamomileResponse<>();
}
@PostMapping(path = "/update")
public ChamomileResponse updateAuth(@Validated @RequestBody AuthVO request) throws Exception {
authService.updateAuth(request);
return new ChamomileResponse<>();
}
@PostMapping(path = "/delete")
public ChamomileResponse deleteAuth(@RequestBody List authVOList) throws Exception {
authService.deleteAuth(authVOList);
return new ChamomileResponse<>();
}
}
- 아래는 Resource 표현 방법에 대한 예시 이다.
HTTP Method | Scope | Usage Description | URI Example | Request Message | Response Message |
---|---|---|---|---|---|
GET | Item | GET resource: 특정 상품의 상세 정보를 가져 옵니다. 잘못된 예) /books?bookId=112 (Path variable을 Query String으로 표현하지 않습니다.) | /books/{bookId} | URI | JSON |
GET | List | GET resources: 전체 상품 목록을 가져 옵니다. | /books | URI | JSON |
POST | Item | POST resource: 신규 상품를 등록 합니다. | /books | JSON | HTTP Status |
POST | List | POST resources: 신규 상품를 multiple로 등록 합니다. | /books | JSON | HTTP Status |
PUT | Item | PUT resource: 특정 상품의 정보를 갱신 합니다. 잘못된 예) /books?bookId=112 (Path variable을 Query String으로 표현하지 않습니다.) | /books/{bookId} | JSON | HTTP Status |
PUT | List | PUT resources: 도서 상품를 multiple로 갱신 합니다. | /books | JSON | HTTP Status |
DELETE | Item | DELETE resource: 특정 상품 정보를 삭제 합니다. | /books/{bookId} | URI | HTTP Status |
DELETE | List | DELETE resources: 도서 상품를 multiple로 삭제 합니다. | /books | JSON | HTTP Status |
HTTP Encoding
-
Http Encoding 는 5 가지의 Content Type을 지원한다.
- application/xml
- POST, PUT 요청 시 XML형식의 데이터를 포함할 수 있다.
- application/json
- POST 요청 시 JSON형식의 데이터를 포함할 수 있다.
- application/javascript
- javascript를 포함하는 데이터를 전송하기 위하여 application/javascript Content Type을 사용 할 수 있다.
- application/x-www-form-urlencoded
- API를 호출하여 Parameter를 전달하는 경우 Parameter는 HTML폼의 기본 인코딩 방식인 application/x-www-form-urlencoded 형태로 인코딩 할 수 있다.
- 요청하는 Parameter 수가 적을 경우에만 사용해야 한다.
- multipart/form-data
- 이미지를 포함한 데이터를 전송하기 위해 Content Type을 multipart/form-data로 설정하여 사용 할 수 있다.
- application/xml
HTTP 응답 상태 코드
-
마지막으로 응답 상태코드를 간단히 살펴보도록 하겠습니다. 잘 설계된 REST API는 URI만 잘 설계된 것이 아닌 그 리소스에 대한 응답을 잘 내어주는 것까지 포함되어야 합니다. 정확한 응답의 상태코드만으로도 많은 정보를 전달할 수가 있기 때문에 응답의 상태코드 값을 명확히 돌려주는 것은 생각보다 중요한 일이 될 수도 있습니다. 혹시 200이나 4XX관련 특정 코드 정도만 사용하고 있다면 처리 상태에 대한 좀 더 명확한 상태코드 값을 사용할 수 있기를 권장하는 바입니다.
-
상태코드에 대해서는 몇 가지만 정리하도록 하겠습니다.
상태코드 | |
---|---|
200 | 클라이언트의 요청을 정상적으로 수행함 |
201 | 클라이언트가 어떠한 리소스 생성을 요청, 해당 리소스가 성공적으로 생성됨(POST를 통한 리소스 생성 작업 시) |
400 | 클라이언트의 요청이 부적절 할 경우 사용하는 응답 코드 |
401 | 클라이언트가 인증되지 않은 상태에서 보호된 리소스를 요청했을 때 사용하는 응답 코드 |
(로그인 하지 않은 유저가 로그인 했을 때, 요청 가능한 리소스를 요청했을 때) | |
403 | 유저 인증상태와 관계 없이 응답하고 싶지 않은 리소스를 클라이언트가 요청했을 때 사용하는 응답 코드 |
(403 보다는 400이나 404를 사용할 것을 권고. 403 자체가 리소스가 존재한다는 뜻이기 때문에) | |
405 | 클라이언트가 요청한 리소스에서는 사용 불가능한 Method를 이용했을 경우 사용하는 응답 코드 |
301 | 클라이언트가 요청한 리소스에 대한 URI가 변경 되었을 때 사용하는 응답 코드 |
(응답 시 Location header에 변경된 URI를 적어 줘야 합니다.) | |
500 | 서버에 문제가 있을 경우 사용하는 응답 코드 |
Api Swagger
- RestApi를 편리하게 문서화 해 주고, 이를 통해서 개발자가 편리하게 API를 호출해보고
테스트를 할 수 있는 플러그인 입니다.
Swagger 의존성 추가
org.springdoc
springdoc-openapi-ui
1.6.9
- Swagger를 사용하기위해 pom.xml에 해당 의존성을 추가합니다
Swagger 설정 추가
@Configuration
public class SwaggerConfig {
private static final String BASE_PACKAGE = "net.lotte.chamomile.admin";
@Bean
public OpenAPI openAPI() {
SecurityScheme securityScheme = new SecurityScheme()
.type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")
.in(SecurityScheme.In.HEADER).name("Authorization");
SecurityRequirement securityRequirement = new SecurityRequirement().addList("bearerAuth");
return new OpenAPI()
.components(new Components().addSecuritySchemes("bearerAuth", securityScheme))
.security(Collections.singletonList(securityRequirement));
}
@Bean
public GroupedOpenApi authApi(){
final String url = "auth";
return GroupedOpenApi.builder()
.group(url)
.pathsToMatch("/chmm/"+url+"/**")
.packagesToScan(BASE_PACKAGE+"."+url)
.build();
}
@Bean
public GroupedOpenApi authGroupApi(){
final String url = "auth-group";
return GroupedOpenApi.builder()
.group(url)
.pathsToMatch("/chmm/"+url+"/**")
.packagesToScan(BASE_PACKAGE+".authgroup")
.build();
}
}
- BASE_PACKAGE 변수:
BASE_PACKAGE
상수는 스캔해야 하는 패키지의 베이스 주소를 담고 있습니다. 이 경우에는net.lotte.chamomile.admin
이라는 패키지가 사용되며, Swagger 문서화 과정에서 이 패키지 내의 컨트롤러들을 스캔하게 됩니다.
- OpenAPI Bean:
OpenAPI
타입의 Bean을 정의합니다. 이 Bean은 전체 API에 대한 OpenAPI 사양을 설정합니다. 여기에는 보안 설정도 포함됩니다.- SecurityScheme: JWT 기반의 인증 메커니즘을 설정합니다. HTTP Authorization 헤더를 사용하여
bearer
토큰을 전달하는 방식으로 구성됩니다. - SecurityRequirement:
SecurityScheme
을 사용하기 위해 필요한SecurityRequirement
를 추가합니다. 이를 통해 API를 호출할 때마다 인증 토큰을 제공해야 함을 명시합니다.
- SecurityScheme: JWT 기반의 인증 메커니즘을 설정합니다. HTTP Authorization 헤더를 사용하여
- GroupedOpenApi Beans:
GroupedOpenApi
객체들을 Bean으로 생성합니다. 이 객체들은 특정 API 그룹에 대한 Swagger 문서화 설정을 제공합니다.- authApi:
auth
라는 그룹을 정의하고, URL 경로/chmm/auth/**
에 매칭되는 API를 문서화합니다. 또한,net.lotte.chamomile.admin.auth
패키지를 스캔하여 해당 컨트롤러들을 문서화에 포함시킵니다. - authGroupApi: 또 다른 그룹
auth-group
을 정의하고, URL 경로/chmm/auth-group/**
에 매칭되는 API를 문서화합니다. 여기서는net.lotte.chamomile.admin.authgroup
패키지를 스캔합니다.
- authApi:
Swagger 화면
- 플러그인 설치후 주소창에 호스트:포트/swagger-ui.html를 입력하게되면 다음과 같이 Swagger가 적용된 화면을 볼 수 있다.
Swagger 기능
-
@Operation(summary = "권한 목록 요청", description = "권한의 목록을 조회합니다.")
-
캐모마일의 경우 스웨거는 필요시 별도의 인터페이스를 구성해서 진행합니다.
@Operation(summary = "권한 목록 요청", description = "권한의 목록을 조회합니다.")
ChamomileResponse> getAuthList(AuthQuery request, Pageable pageable);
@Schema
- 해당 필드가 어떤 의미인지 적을수 있습니다.
public class AuthQuery {
@Schema(description = "권한아이디", example = "admin")
private String searchRoleId; //권한아이디(검색)
@Schema(description = "권한명", example = " ")
private String searchRoleName; //권한명(검색)
}
@ApiModelProperty
-
어노테이션을 적용하게되면 다음처럼 확인할 수 있습니다..
-
테스트를 하려면 우측상단에 Try it out버튼을 클릭해 봅니다.
-
테스트를 하고나면 해당 API를 호출했을때 응답데이터가 정상적으로 내려온걸 확인할 수 있고json확장자의 파일로 API 결과를 다운로드 받을 수 있습니다.
-
적용 예제
-
API 들의 스펙을 명세, 관리, 테스트를 위해 Swagger UI 를 사용 합니다.
API 에서의 Swagger 사용 예제 코드
@Tag(name = "캐모마일 어드민 권한 AuthAPI")
public interface AuthControllerDoc {
@Operation(summary = "권한 목록 요청", description = "권한의 목록을 조회합니다.")
ChamomileResponse> getAuthList(AuthQuery request, Pageable pageable);
@Operation(summary = "권한 생성 요청", description = "권한를 생성합니다.")
ChamomileResponse createAuth(@RequestBody(
content = @Content(
examples = @ExampleObject(value = "{\n" +
" \"authId\": \"test1\",\n" +
" \"authName\": \"테스트1\",\n" +
" \"useYn\": \"1\"\n" +
"}")
)
) AuthVO request) throws Exception;
@Operation(summary = "권한 수정 요청", description = "권한를 수정합니다.")
ChamomileResponse updateAuth(@RequestBody(
content = @Content(
examples = @ExampleObject(value = "{\n" +
" \"authId\": \"test1\",\n" +
" \"authName\": \"테스트1\",\n" +
" \"useYn\": \"1\"\n" +
"}")
)
) AuthVO request) throws Exception;
@Operation(summary = "권한 삭제 요청", description = "권한를 삭제합니다.")
ChamomileResponse deleteAuth(@RequestBody(
content = @Content(
examples = @ExampleObject(value = "[{\n" +
" \"authId\": \"test1\"\n" +
"}]")
)
) List request) throws Exception;
@Operation(summary = "권한 중복확인 ", description = "권한 중복확인을 합니다. ")
ChamomileResponse authCheckId(@RequestBody(
content = @Content(
examples = @ExampleObject(value = "{\n" +
" \"searchAuthId\": \"test1\"\n" +
"}")
)
) AuthQuery request) throws Exception;
@Operation(summary = "엑세파일 저장 ", description = "엑셀 파일을 저장합니다.")
ResponseEntity excelDownload(AuthQuery request) throws Exception;
@Operation(summary = "엑세템플릿 저장 ", description = "엑셀 템플릿을 저장합니다.")
ResponseEntity excelSample() throws Exception;
@Operation(summary = "엑셀 업로드 ", description = "엑셀을 업로드 합니다.")
ChamomileResponse excelUpload(MultipartFile request) throws Exception;
}
@Data
public class AuthQuery {
@Schema(description = "권한아이디", example = "admin")
private String searchAutId; //권한아이디(검색)
@Schema(description = "권한명", example = " ")
private String searchAuthName; //권한명(검색)
@Schema(description = "사용여부", example = " ")
private String searchUseYn; //사용여부(검색)
}
공식 홈 : https://swagger.io/