현재 스웨거 문서로만 적용이 되어 있는데 아무래도 문서 명세서로 보기에는 restdocs이 편할 듯하여 내가 주로 담당하는 리소스에 적용할려고 한다.
일단 스웨거와 restDocs 의 차이
Spring Rest Dcos
장점
실제 코드에 영향이 없음
테스트코드 성공시에만 문서작성이 완료됨.
단점
적용하기 어려움
Swagger
장점
API 테스트화면 제공
적용이 매우 쉬운편
단점
실제 코드(프로덕션)에 어노테이션 추가
실제코드와 동기화 안될 수 있음
와 같은 특징이 있지만 실제로 써본 결과 프론트 엔드 쪽 작업자가 있는 경우 스웨거가 있는것이 좀더 편하다고 하는사람이 많았다.
그러나 퇴사자 혹은 입사자가 많이 생기는 경우 restDoc 문서를 만들어 두는것이 파악하기에는 훨씬 좋다고 느껴져
2가지 다 적용이 되면 좋을 것 같다.
restDocs 작성 방법
작성에 앞서 몇까지 선택 해야하는 것들이 있는데
1. AsciiDoc VS Markdown => 일단 가이드 문서에 있는 adoc을 사용 했는데 markdown도 사용이 가능 한걸로 확인했다.
장점은 작성이 쉽다는데 공식문서를 따르기로 했다.
2. MockMvc(@WebMvcTest) VS Rest Assured(@SpringBootTest) => 문서 작성시 Mocking을 사용하여 작성
Rest Assured 는 BDD 스타일로 직관적이지만 별도의 구성없이는 @SpringBootTest 로 수행
전체 컨테스트를 로드하여 빈을 주입하기에 속도가 많이 느림.
MockMvc 는 @WebMvcTest 수행 controller Layer만 테스트하여 속도가 빠름
단순 컨트롤러 테스트만 할 경우 MockMvc가 좋고 아닌 경우 는 Rest Assured가 좋음
JUNIT 외에 Spock라는게 있는데 작업을 할 프로젝트에는 Jnut 이기때문에 고려하지 않기로 한다.
적용 Project 버전 확인
Sprting boot 2.6.7
java 17
gradle 7
build.gradle 설정 추가
실제 적용된 코드
id "org.asciidoctor.jvm.convert" version "3.3.2" //asciidoc 파일 변환 및 복사 플러그인
configurations {
asciidoctorExt
}
ext {// snippets 파일이 저장될 경로 변수 설정
snippetsDir = file('build/generated-snippets')
}
//dependency 추가
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor:2.0.6.RELEASE'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc:2.0.6.RELEASE'
test {// 테스트 실행 시 snippets를 저장할 경로로 snippetsDir 사용
outputs.dir snippetsDir
}
// API 문서 생성
asciidoctor {
dependsOn test
inputs.dir snippetsDir
attributes 'snippets': snippetsDir
}
tasks.register('copyDocument', Copy) {
dependsOn asciidoctor
from file("build/docs/asciidoc")
into file("src/main/resources/static/docs")
}
build {
dependsOn copyDocument
}
bootJar {
dependsOn asciidoctor
copy {
from "${asciidoctor.outputDir}"
into 'src/main/resources/static/docs'
}
}
외부 연동 리소스 작업중 간헐적으로 443 NoHttpResponseException 에러가 떨어졌다.
타겟 서버에서 유효하지 않은 HTTP 응답으로 제대로 응답하기를 실패했다는 신호라고 나오는데
더 알아보면 HTTP 1.1 / KEEP -ALIVE가 보장될떄 나올 수 있는 오류이다.
즉 , 커넥션이 유지될떄 호출이 되면 해당 타겟 서버에서는 이미 응답을 줬다는 의미로 해당 exception 이 떨어진다.
최초에는 커넥션 을 빠르게 끊고 다시 맺는 옵션을 추가하였습니다.
final int INVALID_CONNECTION_CLOSE_TIME = 5 * 1000;
manager.setValidateAfterInactivity(INVALID_CONNECTION_CLOSE_TIME);
//위 옵션을 restTemplate bean 생성이 추가
위와 같은 조치를 했지만 여전히 발생을 했습니다.
여러 레퍼런스와 스택오버플로우를 보니 위와 같은 문제 발생시 자제 재 호출하는 방법 밖에 없다는 결론에 도달했습니다.
API 구현시 적은 트래픽에서는 문제가 없지만 트래픽이 늘어나면 hikari pool connection timeout 발생으로 요청을 처리하지 못하는 문제를 겪은 적이 있다.
모니터링을 하다보면 hikariPool 에서 connection을 얻을 수 없다고 한다.
이런 경우 나를 포함해서 통상 이렇게 해결을 했을 것이다.
pool 사이즈를 늘려서 해결
사이즈를 늘려서 해결 할 수 있지만 이게 과연 정답인지에 대하여 생각을 해볼 필요가 있다.
Deadlock 발생으로 인하여 생기는 문제
트래픽이 몰리는 상황에서는 Threand간 커넥션을 차지 하기위해서 레이스 커넥션이 발생
한 트랜잭션에 2개의 커넥션이? 필요
우리가 생각할때 하나의 트랜잭션당 하나의 풀만 있으면 될것 같지만 그렇지 않기떄문에 풀사이즈를 일단 늘리면 해결이 되지만 정확한 숫자는 하단의 공식을 참조하는게 좋다.
우리가 생각할때 하나의 트랜잭션당 하나의 풀만 있으면 될것 같지만 그렇지 않기떄문에 풀사이즈를 일단 늘리면 해결이 되지만 정확한 숫자는 하단의 공식을 참조하는게 좋다
같이 일을 했었던 분이 알려준 참고하라는 공식
우형에서 사용하는 hikaricp 풀사이즈 공식
Tn : cpu 쓰레드 갯수
cm : 하나의 task에서 동시에 필요한 커넥션 갯수
pool size = Tm * (Cm-1) + (Tn/2)
히카리에서 제안하는 계산공식
Tm * (Cm-1) + 1
일을 하고 문제를 확인하고 해결을 할떄마다 항상 새로운걸 배우고 공부하게되는데 이런걸 먼저 확인하고 해결하는사람들은 참 존경스럽다..