분류 전체보기 104

SpringBoot 3.* Swagger 적용 방법

SpringBoot 프로젝트 SpringBoot 프로젝트에서 swagger-ui를 적용하려면 build.gradle에 아래의 의존성을 추가하면 됩니다. 의존성 추가 org.springdoc springdoc-openapi-starter-webmvc-ui 2.4.0 implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.4.0' 다른 설정이 없다면 아래의 경로로 API DOCS에 접근할 수 있습니다. http://127.0.0.1:8080/swagger-ui/index.html API DOCS URL 수정 만약 진입 경로를 수정하고 싶으면 application.properties # swagger-ui custom path springdoc..

DEV/Spring MVC 2024.03.23

헥사고날 아키텍처 시리즈 9. 주의사항

헥사고날 아키텍처를 적용해보면서 제가 직접 고민하고 깨닫고 실수했던 사항들입니다. 직접 프로젝트를 하면서 생각나는 부분들이 있으면 채워두겠습니다. 구조 관점 멀티 모듈에서 모듈은 배포 단위로 구성하는 것이 좋습니다. 프로젝트가 모듈 A,B,C로 나누어져있고 모듈 C가 모듈 A,B를 의존하고 있는 상황이라면 하나의 모듈 안에 패키지 구조로 세부 도메인 A,B,C를 나누는 것을 고려해보아야합니다. 세부 도메인을 별도의 모듈로 나누는 시점은 배포를 따로 해야하는 타이밍일 수 있습니다. 애플리케이션 계층 트랜잭션 처리는 애플리케이션 계층에서 이루어저야한다. @Transactional을 사용한다면 AOP의 특징을 반영한 아래의 주의 사항을 참고하자 // TODO @Transactional 사용시 주의사항 트랜잭션..

데이터베이스 트랜잭션과 락 2

트랜잭션의 격리 수준 DIRTY READ NON-REPEATABLE READ PHANTOM READ 1. READ UNCOMMITTED O O O 2. READ COMMITTED O O 3. REPREATABLE READ O 4. SERIALIZABLE 1. READ UNCOMMITTED 커밋되지 않은 데이터를 읽을 수 있습니다. 여러개의 트랜잭선이 동시에 수행되고 있을 때, 다른 트랜잭션에서 수정하고 아직 커밋하지 않은 데이터를 조회할 수 있습니다. 이를 DIRTY READ라고 합니다. 트랜잭선 A에서 수정하고 커밋하지 않은 데이터가 트랜잭션 B에서 DIRTY READ된 뒤에 커밋되지 않고 롤백된다면 데이터 정합성에 문제가 발생할 수 있습니다. 2. READ COMMITTED 커밋된 데이터만..

DEV/Spring Data JPA 2024.03.19

MySQL의 락

글로벌 락 글로벌 락은 MySQL에서 제공하는 락 중에서 가장 범위가 큽니다. 일단 하나의 세션에서 글로벌 락을 획득하면 다른 세션에서 SELECT를 제외한 다른 대부분의 DDL, DML 문장이 대기 상태로 됩니다. 글로벌 락이 영향을 미치는 범위는 MySQL 서버 전체이며, 작업 대상 테이블이나 데이터베이스의 범위를 넘어섭니다. 글로벌 락은 여러 데이터베이스에 존재하는 MyISAM이나 Memory 테이블에 대해서 mysqldump로 일관된 백업을 받아야 할 때 사용합니다. InnoDB는 트랜잭션을 지원하기 때문에 일관된 데이터 상태를 위해 모든 데이터 변경 작업을 멈출 필요는 없습니다. 글로벌 락은 FLUSH TABLES WITH READ LOCK 명령어로 획득할 수 있습니다. 이 작업은 모든 읽기 작..

DB/MySQL 2024.03.18

데이터베이스 트랜잭션과 락 1

트랜잭션과 락 트랜잭션과 락이 비슷한 개념으로 헷갈릴 수 있지만 목적에서 분명한 차이가 있습니다. 트랜잭션은 데이터의 정합성을 보장해주는 기능이고 락은 동시성을 제어하기 위한 기능입니다. 트랜잭션은 작업의 완정성을 보장해줍니다. 논리적인 작업 셋을 모두 처리하거나 모두 처리되지 않도록 처리해줍니다. 락은 동시성을 제어하기 위한 기능입니다. 만약 하나의 회원 정보에 여러 커넥션이 동시에 접근했을 때 락이 없으면 데이터를 동시에 변경하게 됩니다. 락은 여러 커넥션이 동시에 동일한 자원을 요청할 때 한 시점에는 하나의 커넥션만 변경할 수 있도록 보장해주는 역할을 합니다. 더 나아가서, 트랜잭션의 격리 수준은 여러 트랜잭션 사이에서 작업 결과를 어떻게 공유하고 차단할 것인가를 결정하는 레벨입니다. 트랜잭션의 범..

DEV/Spring Data JPA 2024.03.18

데이터베이스 트랜잭션 기초

데이터베이스 종류에 따른 인터페이스 통합 애플리케이션에서 데이터베이스를 사용할 때, 커넥션 연결 → 쿼리 전달 → 응답 반환의 흐름을 사용한다. 그런데 데이터베이스마다 커넥션을 연결하는 방법이 다르다. 이를 통일하기 위해서 JDBC 표준 인터페이스가 만들어졌다 (DriverManager) 일반 커넥션과 커넥션 풀의 도입 데이터베이스 커넥션을 사용할 때 커넥션 풀을 사용한다 직접 커넥션을 연결할 수도 있고, 커넥션 풀을 사용할 수도 있다. 커넥션을 조회하는 방법을 추상화한게 DataSource이다 DriverMangerDataSource HikariDataSource 트랜잭션의 도입 DataSource에서 가져온 커넥션에 대해서 JDBC 코드를 반복해서 작성해야 한다 자동커밋과 수동 커밋에 대한 옵션을 직..

DEV/Spring Data JPA 2024.03.18

Open Session In View

정의 OSIV란 데이터베이스 커넥션 시작 시점부터 API 응답이 끝날 때까지 영속성 컨텍스트와 데이터베이스 커넥션을 유지하는 것을 말합니다. Service 계층을 벗어난 범위에서 지연 로딩이 가능하려면 영속성 컨텍스트가 살아있어야 하고 영속성 컨텍스트는 데이버베이스 커넥션이 유지되어야 가능합니다. OSIV를 사용하면 View Template과 API 컨트롤러에서 지연로딩이 가능해집니다. 설정 springboot에서 OSIV설정의 기본 값은 true 입니다. spring.jpa.open-in-view:true Open Session In View : 하이버네이트 Open EntityManager In View : JPA OSIV를 켤 때 ****주의사항 OSIV를 켜두면 너무 오랜 시간 데이터베이스 커넥션..

DEV/Spring Data JPA 2024.03.18

헥사고날 아키텍처 시리즈 8. 영속성 계층에 ORM 적용하기

객체 중심의 개발의 문제점 애플리케이션이 성장하면서 그 내부의 복잡도는 점점 커집니다. 개발자들이 도메인 영역을 객체지향 중심적으로 잘 구성한다고 하더라도, 복잡한 객체가 될수록 객체의 행위를 데이터베이스에 저장하는 과정도 함께 복잡해집니다. 객체지향은 행동을 중심에 두고, 관계형 데이터베이스는 데이터를 중심에 두기 때문에 이 둘 사이의 불일치가 존재합니다. 유연하고 확장 가능하도록 객체지향 설계를 향해 나아갈수록 데이터 중심의 데이터베이스와 거리가 멀어집니다. 추상화, 캡슐화, 정보은닉, 상속, 다형성 등 애플리케이션 개발에서 사용되는 다양한 장치들이 데이터베이스에는 정확하게 매핑되는 개념이 없기 때문입니다. 객체를 관계형 데이터베이스에 저장하고 조회하는 과정에서 개발자는 무수히 많은 코드를 작성하고 ..

헥사고날 아키텍처 시리즈 7. 테스트

헥사고날 아키텍처의 테스트 테스트는 비용이 적고, 유지보수가 쉽고, 빨리 실행되어야 합니다. 그래서 테스트 피라미트가 등장했습니다. 단위 테스트 - 통합 테스트 - 시스템 테스트가 순서대로 있습니다. 단위 테스트는 가장 밑에서 가장 넓은 영역을 차지합니다. 시스템 테스트는 가장 비용이 높은 테스트로서 커버리지의 목표가 통합 테스트와 유닛 테스트보다 좁아야 합니다. 모든 영역의 테스트에서 커버리지를 높게 잡으면 새로운 기능을 만드는 것보다 테스트를 만드는데 더 시간을 쓰게 됩니다. 유닛/통합/시스템 테스트는 각각 클래스/연결된 여러 컴포넌트/모든 컴포넌트를 검증합니다. 단위 테스트 단위 테스트를 수행할 때에는 의존성이 있는 컴포넌트의 동작을 mocking 합니다. 그리고 테스트 대상이 모킹하고 있는 의존 ..

헥사고날 아키텍처 시리즈 6. 영속성 계층

헥사고날 아키텍처의 영속성 계층 영속성 계층 Adapter의 동작 방식 일반적인 의존성 역전을 사용한다면 Service 계층에서 Repository 인터페이스를 호출하고 실질적으로는 RepositoryImpl이 동작합니다. Service -호출→ Repository -구현→ RepositoryImpl 이러한 의존성 역전을 한 번 더 꼬은 것이 Adapter입니다. Adapter는 애플리케이션 계층의 port 인터페이스를 상속한 Repository가 사용되는 곳입니다. Service -호출→ Port -구현→ Adapter -호출→ Repository -구현→ RepositoryImpl package io.lucky.user.persistence.adapter; import io.lucky.user.app..