분류 전체보기 23

PDFBox 실무 적용기 - 좌표계 꼬임 문제와 래퍼 설계

들어가며외부에서 전달받은 PDF 문서에 텍스트나 이미지를 추가로 삽입해야 하는 업무가 있었고 이를 처리하기 위해 PDFBox를 사용해 구현을 시작했습니다.처음에는 단순히 newLineAtOffset(x, y)로 좌표를 이동하고 showText()로 텍스트를 출력하는 방식으로 접근했습니다.하지만 실제로는 다양한 툴에서 생성된 PDF 문서를 다루게 되었고, 각 툴마다 내부 좌표계(CTM, Current Transformation Matrix)가 조금씩 달랐습니다. 일부 문서는 좌표계가 회전된 상태로 저장되어 있기도 했습니다.그래서 제가 의도한 위치가 아닌 전혀 다른 곳에 텍스트나 이미지가 삽입되는 문제가 발생했습니다.이번 글에서는 이 문제를 어떻게 마주했고, 어떤 방식으로 해결했는지를 정리해 보려고 합니다...

Java 2025.08.24

Function Calling으로 LLM에게 메서드 제공하기 (feat. AI Agent)

들어가며Spring AI를 공부하면서 가장 인상 깊었던 기능이 바로 Tool Calling(Function Calling)입니다.그동안은 프롬프트와 RAG를 통해서만 LLM에 외부 정보를 전달할 수 있었지만, 이제는 LLM이 직접 외부에 접근할 수 있는 시대가 열렸습니다.물론 모든 모델이 이 기능을 지원하는 것은 아닙니다. 예를 들어 Ollama에서는 해당 기능이 가능한 모델에 Tools 라벨이 표시됩니다.Spring AI 공식 문서에서는 Tool Calling의 활용 범위를 크게 두 가지로 설명하고 있습니다.정보 검색: 데이터베이스, 웹 서비스, 파일 시스템 또는 웹 검색 엔진과 같은 외부 소스에서 정보를 검색 (예: RAG 구현)조치 수행: 이메일 전송, 데이터베이스에 새 레코드 생성, 양식 제출, ..

Spring 2025.08.10

Advisor로 LLM에 추가 정보 제공 및 응답 가공하기 (feat. RAG)

들어가면서Spring AI는 최근 1.0.0 정식 버전으로 출시되며, 내부 구조와 주요 기능이 대폭 정리되었습니다.그중에서도 LLM을 실무에서 안전하고 유연하게 다루기 위해 반드시 이해해야 할 핵심 기능이 바로 Advisor입니다.Advisor는 LLM 호출 전/후의 데이터를 가공하거나 보강할 수 있는 인터셉터(Interceptor) 역할을 수행합니다.이를 통해 외부 정보를 주입하거나, 민감한 응답을 걸러내는 등의 고급 흐름 제어가 가능합니다.Spring AI의 Advisor는 다음과 같은 작업에 활용됩니다:프롬프트 전처리: 고정 지시문 추가, 외부 문서 삽입(RAG), 로깅 등응답 후처리: 민감한 내용 필터링, 마스킹, 응답 포맷 보정, 로깅 등이번 글에서는 Advisor의 구조와 동작 방식을 예제와 ..

Spring 2025.08.03

CaseBuilder 대신 BooleanBuilder를 써야 했던 이유(Feat: querydsl)

들어가면서최근 QueryDSL로 조회 쿼리에서 응답 속도가 비정상적으로 느려지는 문제를 경험했습니다.원인을 추적해보니, 문제의 핵심은 where 절에 사용한 CaseBuilder였습니다.status 컬럼을 기준으로 조건 분기를 작성했는데, 특정 상태값일 때만 조회가 유독 느렸습니다.이후 BooleanBuilder로 로직을 변경한 결과, 쿼리 속도가 수 초에서 수백 밀리초로 개선되었습니다.사용했던 상황비슷한 상황으로 다음과 같은 예제를 만들었습니다.게시글의 상태(status)가 DELETED, PENDING, ACTIVE 등의 값으로 존재하는 게시판 시스템이 있다고 가정해보겠습니다.A 사용자(예: 관리자)는 status = 'PENDING' 상태의 게시글을 그대로 보아야 하고,B 사용자(예: 일반 직원)에..

DB 2025.07.27

Spring AI로 LLM 연결하기(Feat: ollama)

들어가면서올해 초 LLM에 관심이 생겨 이것저것 찾아보다가 Spring AI를 알게 되었고, 최근 정식 버전인 1.0.0이 출시되면서 본격적으로 공부하게 되었습니다.제가 느끼기에 Spring AI는 LLM 연결을 매우 쉽게 도와주며, 다음과 같은 다양한 기능을 제공합니다:프롬프트 전처리/후처리를 위한 AdvisorJSON → DTO 구조화 출력 지원LLM이 메서드를 직접 호출하는 Function CallingRAG, MCP, 멀티모달 등 다양한 고급 기능 지원덕분에 이제는 Python 기반 도구가 아니더라도, Java 환경에서도 LLM을 효과적으로 활용할 수 있게 되었습니다.이 블로그 시리즈는 총 3편으로 구성될 예정이며, 각 주제는 다음과 같습니다:Spring AI로 LLM 연결하고 구조화된 출력하기A..

Spring 2025.07.27

[Java] Stream 생성하는 방법들

들어가면서오늘은 Stream 생성하는 방법에 대해서 정리하겠습니다.1. 컬렉션(List, Set등)으로 스트림 생성2. 배열로 스트림 생성3. Stream.of, Stream.ofNullable(java 9)로 스트림 생성4. 무한 스트림(iterate)으로 스트림 생성5. 무한 스트림(generate)으로 스트림 생성6. 스트림 빌더로 스트림 생성7. 빈스트림으로 스트림 생성8. Stream 연결로 스트림 생성9. 기본형 특화 스트림(Int, Long, Double)으로 스트림 생성컬렉션(List, Set등)으로 스트림 생성List, Set등 Collection을 상속받은 객체에서 사용할 수 있는 방법입니다.// Collection 인터페이스에 정의됨default Stream stream() { ..

Java 2025.05.06

Querydsl 다중 where 조건 만들기

들어가면서Querydsl의 장점 중 하나는 쿼리를 Java로 쿼리를 만들 수 있다는 것입니다.그래서 where문에 사용되는 다양한 조건을 메서드화하여 조립하여 만들어 가독성도 높아집니다.특정 조건같은 경우는 다양한 조회에서 사용할 수 있기 때문에 재사용성도 높아집니다.사용방법간단하게 User를 조회할 때 이메일과 이름으로 조회하는 경우로 사용법을 작성하겠습니다.// 메서드화하지 않은 버전 이메일 and 이름 조건 User user = queryFactory.selectFrom(user) .where(user.email.eq("test@test.com") ,user.name.eq("홍길동")) ..

DB 2025.04.23

EXISTS 연산자에 대해서(Feat: querydsl)

들어가면서EXISTS 연산자에 대해서 기본인 조건에 존재하는 것이 있으면 'True' 결과 전달한다는 내용만 알고 어떤 상황에서 사용하는지를 정확하게 몰랐다고 생각됩니다.그냥 INNER JOIN이나 OUTER JOIN을 주로 사용해서 exists를 거의 사용하지 않았습니다.최근 쿼리를 개선하다보니 join이 아닌 exists를 사용하여 속도 개선이 많이 발생한 내용을 공유드립니다.exists는 언제 사용해야할까?위에서 적은 것처럼 존재하면 'True'로 반환하기 떄문에 조건에 따라 데이터를 걸러내어 결과를 조회할 때 사용되고 있습니다.사실 IN 연산자를 이용하여 조회해도 동일한 결과를 얻을 수 있지만 서브쿼리 결과가 100개 이상 나올 가능성이 있으면 IN 연산자를 사용할 수 없습니다.IN 연산자가 SE..

DB 2025.04.13

[Spring] Factory Method Pattern 사용하기 (Feat: injecting Collections)

Factory Method Pattern이란?팩토리 메소드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 구현체의 인스턴스를 만들지는 구현체 클래스에서 결정하게 만드는 패턴입니다.결합도를 낮추고 캡슐화와 추상화를 통해 유지보수성을 높이기 위해서 사용됩니다.Factory Method Pattern의 역할생성 과정을 캡슐화하여 생성 과정의 변경 사항은 호출자에게 투명성을 가집니다.생성 과정을 재사용할 수 있습니다.복잡한 생성 과정을 캡슐화하므로, 호출자는 객체 생성 방벙을 알 필요가 없습니다.생성 과정과 사용 과정을 분리하여 복잡한 코드를 간결하게 바꿀 수 있습니다.injecting Collections이란?Spring에서 선언됨 Interface에 여러개의 구현체가 있을 경유 List, S..

Spring 2025.03.23

[JPA] 엔티티 값을 변환하여 저장, 조회하기(Feat: @Converter)

어떤 값을 저장할 때 데이터베이스에 저장할 때 원하는 값으로 저장하고 조회할 때도 원하는 값이나 타입으로 반환할 때 사용합니다.예를 들어 개인정보의 암호화하여 저장해야하거나 Boolen 타입일 때 Y, N 값으로 데이터베이스에 저장하는 등 다양한 경우에 사용될 수 있습니다.AttributeConverter란AttributeConverter 클래스는 프로퍼티의 값을 저장하거나 읽을 때 사용할 수 있는 인터페이스입니다.public interface AttributeConverter { // 엔티티의 데이터를 데이터베이스 컬럼에 저장할 데이터로 변환한다. Y convertToDatabaseColumn(X attribute); // 데이터베이스에서 조회한 컬럼 데이터를 엔티티 데이터로 변환한다...

DB 2025.03.10