RAG 질의 연동이 가능한 드라이브 시스템 및 벡터 임베딩 스케줄러
드라이브 시스템사용자는 개인 드라이브에 폴더를 만들거나 파일을 업로드할 수 있다. 이렇게 업로드된 파일은 벡터 임베딩 스케줄러에 의해 Fast API를 호출해서 벡터 임베딩되고, Qdrant 벡터 저장소에 저장되어 RAG가 질의를 할 때 참고할 수 있도록 한다.드라이브 시스템을 구현할 때 고려해야 했던 부분은, 어떻게 해야 효율적으로 계층적인 폴더 구조를 사용자에게 제공해서 보여줄 수 있는지 였다.이를 위해 JPA에서 폴더 내에 어떤 파일과 폴더가 있는지 그리고 본인의 부모 폴더가 누구인지 연관관계를 맺어주었다.하지만 폴더의 계층 구조를 부모 폴더 및 자식 폴더의 연관관계를 통해 접근해서 조회하면, 쿼리가 재귀적으로 나가기 때문에 폴더 깊이가 깊어질수록 성능이 좋지 않다는 문제가 있었다.따라서 이를 해결..
2026.02.08
[인프런 김영한] 자바 예외
예외 계층 최상위 예외 객체인 Throwable을 상속받는 Exception과 Error 중에, 잡아야 하는 예외는 Exception 이다. Error는 잡으려고 하면 안되기 때문이다.Exception은 체크 예외 (컴파일 시점에서 체크하는), RuntimeException은 언체크 예외 (컴파일 시점에 체크하지 않는) 체크 예외public class CheckedAppTest { @Test void checked() { Controller controller = new Controller(); Assertions.assertThatThrownBy(controller::request) .isInstanceOf(Exception.class);..
2026.02.08
[인프런 김영한] 스프링과 트랜잭션
순수한 서비스 계층서비스 계층은 특정 기술에 종속적이지 않게 개발해야 한다. 즉, 기술에 종속적인 부분은 프레젠테이션 계층(UI), 데이터 접근 게층에서 가지고 간다.이렇게 해야, 나중에 기술이 변경되더라도 비즈니스 로직을 담당하는 서비스 계층은 수정하지 않을 수 있다. 트랜잭션 동기화 매니저 스프링은 트랜잭션 동기화 매니저를 제공해서, 한 트랜잭션 내에서 동일한 커넥션을 사용하도록 한다.트랜잭션 매너지가 데이터소스를 통해 커넥션을 만들면 트랜잭션 동기화 매니저에 보관하고, 레포지토리가 이 트랜잭션 동기화 매니저에 저장된 해당 커넥션을 가져다 쓰는 방식으로 동일한 커넥션을 유지하는 것.덕분에 파라미터로 커넥션을 넘기지 않아도 된다.트랜잭션 동기화 매너지가 관리하는 커넥션이 없으면, 새로운 커넥션을 ..
2026.02.05
[인프런 김영한] 커넥션 풀과 트랜잭션
커넥션 풀을 사용하는 이유DB에 쿼리를 하나 날리기 위해서는 아래와 같은 과정을 거쳐야 한다.TCP/IP 연결 수립 (3-way handshake): DB 서버와 네트워크 연결을 맺는다. 물리적으로 시간이 가장 많이 걸린다.ID/PW 인증: 전달받은 계정 정보를 DB 내부적으로 확인한다.DB 세션 생성: DB가 해당 클라이언트를 위한 메모리와 리소스를 할당한다.SQL 실행: 실제 원하는 작업을 수행한다.연결 종료: 리소스 해제 및 TCP 연결 종료실제 중요한 4번보다, 1~3번 과정의 준비 과정(연결 수립) 시간이 더 오래 걸리는 배보다 배꼽이 더 큰 상황이 발생하므로 커넥션 풀을 사용한다. 커넥션 풀TCP/IP 연결을 수립하는 과정에서 시간이 소요되므로, 미리 생성해두고 사용하는 것. 보통 10개이다..
2026.02.01
운영 중인 시스템의 1:1 구조를 1:N으로 바꾸기
현장실습을 시작한지 어느덧 2주가 흘렀다. 기존에 만들어져 있는 프로젝트 위에서 엔티티에 속성을 추가해서 구현하거나, 변경된 데이터 형식에 맞춰 DTO 바꾸면서 서비스 계층 로직을 수정하고 API를 별도 추가하는 등의 구현에 어느 정도 익숙해진 참이었다. 그러다 이번에 리뷰&코멘트 기능을 기존 1:1 구조(단일 댓글)에서 1:N 구조(다중 댓글)로 변경하는 업무를 맡게 되었다. 원래 내 담당은 아니었지만, 배정된 초기 업무를 일찍 마쳐서 일정이 바쁜 다른 직원분의 업무를 지원하게 되었다. 엔티티 설계를 많이 해본 것이 아니었기에 잘 할 수 있을지 걱정이었다. 하지만 단순히 별도 테이블을 만들어 연결해주면 된다고 생각했기에 크게 어렵지 않을 것이라 예상했다. 하지만 막상 코드를 열어보니 상황은 생각보다 ..
2026.01.14
no image
[인프런 김영한] OSIV와 성능 최적화
OSIV (Open Session In View) JPA는 트랜잭션이 시작될 때 데이터베이스 커넥션을 가져온다.osiv가 켜져있으면,트랜잭션이 끝나도 데이터베이스 커넥션을 반환하지 않는다(영속성 컨텍스트를 살려둔다).뷰의 경우 데이터가 렌더링 되고 나서, API의 경우 응답이 요청에 완전히 반환되는 등 모든 작업이 완전히 끝이 나야 돌려준다.이러한 osiv가 default로 true 이기 때문에, View Template이나 API 컨트롤러에서도 지연 로딩이 가능했던 것.지연 로딩은 영속성 컨텍스트가 살아있어야 가능하고, 영속성 컨텍스트는 데이터베이스 커넥션을 유지한다.이렇게 유용하지만, 데이터베이스 커넥션 리소스를 너무 오랫동안 붙들고 있기 때문에 실시간 트래픽이 중요한 경우 커넥션이 모자랄 수 있고 ..
2025.11.25
[인프런 김영한] 지연 로딩과 조회 성능 최적화
지연 로딩과 조회 성능 최적화xToOne(OnetoOne, ManyToOne) 관계일 때 최적화 하기 엔티티를 그대로 반환Order Entity@Entity@Getter @Setter@Table(name = "orders")public class Order { @Id @GeneratedValue @Column(name = "order_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") private Member member; @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) private List ord..
2025.11.25
[인프런 김영한] DTO를 이용한 데이터 전달
@Valid 어노테이션과 jakarta 제약 조건을 이용한 입력 값 예외 처리@Entity@Getter@Setterpublic class Member { @Id @GeneratedValue @Column(name = "member_id") private Long id; // 제약 조건 @NotEmpty private String name; @Embedded private Address address; @OneToMany(mappedBy = "member") private List orders = new ArrayList();} @RestController@RequiredArgsConstructorpublic class MemberApiController {..
2025.11.19
no image
[인프런 김영한] 준영속 엔티티 그리고 변경 감지와 병합
준영속 엔티티란? 영속성 컨텍스트는 엔티티를 영구 저장하는 환경이다. 엔티티가 이 컨텍스트 안에 있으면 영속 상태, 밖으로 나오면 준영속 상태가 된다.트랜잭션 안에서 엔티티의 값이 변경되면 JPA에서 이를 감지하고, COMMIT 시점에 해당 변경사항을 DB에 자동으로 반영해준다.JPA 영속성 컨텍스트가 위처럼 더 이상 관리하지 않는 엔티티(Primary Key 등의 식별자 값이 이미 존재하는 것 처럼 DB에 이미 한 번 저장된 적이 있는 데이터)를 준영속 엔티티라고 한다.임의로 만든 엔티티라도, 식별자 값이 있으면 (엔티티 객체를 만들고 해당 엔티티에 식별자 값을 대입한 경우 등) 이 또한 준영속 엔티티에 해당된다.@PostMapping("/items/{itemId}/edit")public String ..
2025.11.18