프로젝트 목록
02 / BACKEND · DATA INTEGRATION

공연잇다

흩어진 공연 데이터를
탐색 가능한 정보로.

공공 공연 데이터를 수집하고 탐색하는 서비스. 외부 데이터 변환·저장, 기존 데이터 보완, AI 요약 흐름과 보안 연동을 개선했습니다.

프로젝트
2026 · 팀 프로젝트
팀 구성·역할
5명 · 팀장
개인 담당
공공데이터 변환·보완, AI·보안 흐름 개선
공연잇다 프로젝트 실제 서비스 화면
공연잇다 실제 실행 화면뮤지컬 필터가 적용된 기존 데이터 조회 화면입니다. 포스터·기간·장소·가격과 검색 UI를 보여 주며, 기간 경계 조건의 실행 검증을 대신하지 않습니다.
01OVERVIEW

수집 이후의 데이터를 다루다.

KOPIS와 대전광역시 공공데이터는 응답 형식과 정보의 빈 정도가 다릅니다. 데이터를 공통 공연 정보로 변환하고, 다시 수집할 때 기존 정보를 보존하면서 부족한 값을 보완했습니다.

직접 구현·수정

외부 응답 Mapper와 수집·저장 흐름, 기존 데이터의 빈 필드 보완, 상세 AI 요약·추천 이유 흐름과 기존 보안 구성을 보완했습니다.

팀 기능과의 연결

기간 겹침 검색 조건은 팀원이 추가한 기능입니다. AI와 인증에도 여러 작성자의 이력이 있으며, 담당한 보완·연동 변경을 별도로 연결했습니다.

02DATA PIPELINE

다시 들어온 데이터,
무조건 덮어쓰지 않기.

문제

외부 데이터를 반복 수집할 때 같은 출처의 공연이 다시 들어옵니다. 새 응답에 값이 빠져 있다고 기존에 저장한 정보까지 잃어서는 안 됩니다.

판단

동일 출처의 재수집을 식별하는 기준을 sourceName + externalId로 두었습니다. 신규 저장과 기존 데이터 보완을 분리하고, 이미 존재하는 값은 보존합니다.

  1. 출처와 외부 ID로 조회

    findBySourceNameAndExternalId로 기존 공연을 찾습니다.

  2. 새 공연이면 저장

    생성·갱신 시점을 설정하고, 가능한 경우 요약 생성을 시도한 뒤 저장합니다.

  3. 기존 공연이면 빈 값 보완

    제목·장르·포스터·상세 정보·기간 등 비어 있는 필드를 보완합니다. 변경이 있을 때 갱신 시점을 바꿉니다.

동일 출처에서 반복 수집된 항목을 처리하는 방식입니다. 서로 다른 기관의 같은 공연을 유사도 분석으로 통합하는 기능은 아닙니다.

날짜와 장르의 정규화

KOPIS 날짜는 yyyy-MM-dd, yyyy.MM.dd, yyyyMMdd 순서로 파싱합니다. 파싱 실패는 null로 처리하고, 원본 장르와 서비스용 장르를 구분해 저장합니다.

재수집 처리의 핵심 코드 보기
// PerformanceSyncService.java · 핵심 흐름 발췌
Optional<Performance> existing =
    performanceRepository.findBySourceNameAndExternalId(
        incoming.getSourceName(), incoming.getExternalId());

// 신규 항목: 생성·갱신 시점 설정 후 저장
// 기존 항목: enrichBlankFields(target, incoming)
// 실제 변경 시: target.setUpdatedAt(LocalDateTime.now())

원본 전체는 관련 코드 링크에서 확인할 수 있습니다.

04AUTHENTICATION

공개 조회와 사용자 기능의 경계.

기존 Spring Security·JWT 구성에서 공개할 조회와 인증이 필요한 기능의 경계를 보완했습니다. JWT 파싱 이후 DB의 currentToken과 비교하고, 인증 실패 응답 형식을 통일합니다.

공개 조회

공연 목록·검색
GET /api/performances

숫자 ID의 공연 상세
GET /api/performances/{id}

인증 후 사용

내 정보
GET /api/auth/me

관심 공연 /api/favorites
추천 GET /api/recommendations

공통 인증 실패 응답

HTTP 401
{
  "success": false,
  "code": "UNAUTHORIZED",
  "message": "인증이 필요합니다."
}

코드에서 확인한 응답 형식입니다. 이번 작업에서 실서버를 호출해 캡처한 응답은 아닙니다.

05AI INTEGRATION

저장한 요약은 재사용하고,
누락된 정보는 보완하다.

요약과 추천 이유의 생성 시점은 하나가 아닙니다. 동기화 과정과 상세 조회에서 필요한 값을 보완하고, 유효한 저장값이 있으면 재사용합니다.

시점동작
데이터 동기화원문 설명과 요약 유무를 확인해 요약 생성 시도.
상세 조회요약·추천 이유가 없거나 기본 문구일 때 생성 시도. 성공하면 저장.
유효한 저장값 존재저장된 값을 재사용. 목록도 저장값 사용.
빈 응답·사용 불가 응답·예외빈 결과 처리. 상세 DTO가 정해진 기본 문구 제공.
결과를 얻지 못했을 때의 표시요약: “요약 정보를 준비 중입니다.”
추천 이유: “선택한 조건에 맞는 공연입니다.”

추천 후보와 AI 설명의 역할 분리

추천 후보는 사용자의 선호 장르와 DB 조회로 선택합니다. AI는 요약과 추천 이유 생성의 역할을 맡으며, 전체 공연의 순위를 결정하는 구조로 설명하지 않습니다.

겨울왕국 겨울이야기 대전 공연의 정보와 AI 요약이 표시된 상세 화면
저장된 AI 요약의 상세 화면기존 DB에 저장된 공연 정보와 요약을 조회한 화면입니다. 이 캡처는 새로운 외부 AI 호출 성공이나 개인화 추천의 품질을 입증하지 않습니다.

여기서 설명한 실패 처리는 코드의 예외·대체 응답 범위입니다. 외부 AI 장애 시 전체 서비스의 정상 동작을 실행 검증했다는 의미는 아닙니다.

06REFLECTION

데이터의 출처와
빈 값의 의미를 구분하는 일.

수집 결과를 저장하는 것과 기존 데이터를 보존하는 것은 다른 문제였습니다. 데이터의 출처·식별자·빈 필드 처리 기준을 명확히 해야 반복 수집 이후의 상태를 설명할 수 있었습니다.

AI 결과 또한 생성 성공 여부만으로 끝나지 않았습니다. 생성 시점, 재사용할 값의 조건, 실패했을 때 사용자에게 표시할 문구를 함께 연결했습니다.

협업에서의 역할

5명 팀의 팀장으로 역할과 API 응답 규격을 정리하고, 기능 브랜치를 통합하며 병합 충돌을 해결했습니다. 문서를 전달하는 데서 끝내지 않고 함께 코드를 보며 막힌 부분을 확인하는 방식으로 협업을 개선했습니다.

다음 프로젝트 · 서비스 연동·시뮬레이션

배송잇다

확대된 프로젝트 화면