jieun / signal terminal
? help
단어 · API · CLI 지원

예: 소개 · 기술스택 · 경력 · 나 · 연락처 · 덤핏 보여줘 · help

Project archive / docshund

닥스훈트(Docshund)

개발 문서를 함께 번역하고 지식을 나누는 커뮤니티

visitor > GET /projects/docshund200 OK
← 프로젝트 목록
공식 문서 공동 번역을 소개하고 번역문서·커뮤니티·헬프데스크 메뉴를 제공하는 닥스훈트 홈 화면
DOCSHUND / 공식 문서 공동 번역 서비스의 홈 화면

프로젝트 한눈에 보기

외국어 IT 공식 문서를 문단 단위로 함께 번역하고 지식을 나누는 커뮤니티입니다. 6인 팀의 백엔드 개발자로서 문서·원본 문단·번역의 데이터 관계와 API, Python을 이용한 HTML 분리, 문단별 베스트 번역 조회, 실시간 알림과 개인 메모를 구현했습니다.

개발 기간
2025.01 — 2025.02
프로젝트 형태
6인 팀 · FE 3 / BE 3 (인프라 겸임 1)
기술 스택
Spring Boot · Python · MariaDB
제공 상태
공개 저장소 · 소개 영상 제공

내가 맡은 일

문서 데이터 모델링과 문서·번역 API, HTML 파싱, 베스트 번역 조회, 알림 이벤트·SSE 초기 연결, 메모 API를 맡았습니다. README 초안과 소개 영상도 제작했습니다. SSE 연결 문제의 원인 파악과 최종 수정은 BE 팀원들과 함께 진행했으며, 프런트엔드의 번역 뷰어와 IndexedDB는 FE 담당자가 구현했습니다.

대표 기록

  1. HTML을 문단별 번역 데이터로Python으로 HTML의 본문·제목·목록·코드 요소를 추출하고, 순서가 있는 원본 문단과 사용자 번역을 연결했습니다.
  2. 문단별 베스트 번역 조회좋아요 기준으로 선택한 번역을 원문 순서에 맞추고, 번역 ID 목록으로 좋아요 정보를 일괄 조회해 응답에 결합했습니다.
  3. SSE 구독의 DB 연결 점유 문제알림의 저장·전송을 구현하고, 구독 중 DB 연결 풀이 부족해지는 문제를 BE 팀원들과 함께 수정했습니다.

문서를 읽고 번역을 보태는 흐름

닥스훈트는 외국어 IT 공식 문서를 한국어로 함께 번역하는 커뮤니티입니다. 사용자는 원하는 문단부터 번역에 참여하고, 다른 사용자의 번역을 비교하거나 좋아요로 평가할 수 있습니다. 번역을 모아 읽는 흐름과 자신의 기여에 대한 반응을 확인하는 흐름을 함께 제공합니다.

원문 읽기에서 공동 번역과 개인 기록까지
  1. 문서 선택·읽기

    관심 있는 기술의 공식 문서를 골라 원문과 문단별 번역을 읽습니다.

  2. 번역 작성·좋아요

    원하는 문단에 번역을 남기거나 다른 사용자의 번역에 좋아요를 누릅니다.

  3. 베스트 번역 읽기

    문단별로 선택된 번역을 원문의 순서에 맞춰 읽습니다.

  4. 알림·개인 메모

    내 번역에 대한 반응을 알림으로 확인하고 필요한 내용은 메모로 정리합니다.

백엔드에서는 문서 정보, 원본 문단, 사용자 번역을 구분해 저장했습니다. 하나의 원본 문단에 여러 번역을 연결해 번역을 비교할 수 있도록 하고, 문단의 순서를 유지해 문서 전체를 읽는 흐름으로 이어지도록 했습니다.

담당 범위와 기술

프런트엔드 3명과 백엔드 3명으로 구성된 팀에서 문서·번역, 알림, 개인 메모의 백엔드를 담당했습니다. 백엔드 팀원 중 1명이 인프라를 겸임했으며, 인증·커뮤니티·채팅과 배포 환경은 담당 팀원들과 역할을 나눴습니다.

담당 기능에 사용한 기술
영역기술구현 범위
서버Java 21 · Spring Boot문서·번역·메모 API, 입력 조건과 권한 검사
데이터JPA · QueryDSL · MariaDB문서 관계 모델링, 번역 선별과 좋아요 정보 조회
원본 처리Python · BeautifulSoupHTML 요소 추출과 순서·내용의 JSON 변환
실시간 알림SSE · SseEmitter알림 생성·저장·전송, 사용자별 구독 연결
문서화README · 소개 영상서비스 흐름과 담당 범위 정리, 시연 자료 제작

MariaDB는 프로젝트 개발 당시 기술 구성을 기준으로 적었습니다. 이후 저장소에는 MySQL로 설정을 전환한 이력이 있습니다. 프런트엔드의 원문 저장·재조회에 사용한 IndexedDB는 FE 담당자의 구현입니다.

시스템 구성과 문서 데이터 관계

문서·번역 API와 HTML 처리 프로세스의 연결
  • React 웹

    HTTP로 문서·번역·메모 API 사용 · SSE로 알림 수신 · FE 담당자가 구현한 브라우저 IndexedDB에 원문 저장

공통 API 요청

Spring Boot 백엔드

문서·번역·알림 API · Java가 ProcessBuilder로 서버 내부의 Python 파서 실행

데이터 저장 · 외부 서비스 연동

  • MariaDB

    문서 정보 · 원본 문단 · 사용자 번역과 좋아요 · 개인 메모 · 알림 저장

문서에서 문단별 번역으로 이어지는 데이터 관계
데이터보관하는 정보관계
Document문서 이름·버전·분류·원문 링크·라이선스하나의 문서에 여러 원본 문단 연결
OriginDocument문단 순서 pOrder · HTML 태그 · 원문 내용소속 문서와 연결, 하나의 문단에 여러 번역 연결
TranslatedDocument작성자 · 번역 내용 · 공개 상태번역 대상인 원본 문단과 연결
TranslatedDocumentLike번역에 좋아요를 남긴 사용자번역과 사용자의 관계를 별도로 저장

문서 전체의 정보와 실제 번역 단위를 분리해, 특정 문단에 여러 번역이 쌓여도 원문과의 관계를 유지하도록 했습니다. Python은 Spring Boot가 원본 등록 시 실행하는 처리 프로세스이며, 처리 결과를 받은 뒤 Java에서 원본 문단을 저장합니다.

HTML을 사람이 번역할 수 있는 단위로

문제: 긴 원문과 개별 번역을 연결할 기준이 필요했습니다.

문서 전체를 하나의 긴 텍스트로 저장하면 특정 문단에 번역을 연결하거나, 여러 번역 중 하나를 골라 원문 순서대로 보여주기 어렵습니다. 원문의 구조와 순서를 번역 데이터에 연결할 단위가 필요했습니다.

관리자가 HTML 원본을 등록했을 때의 처리 순서
  1. 원본 파일 등록

    문서 정보를 등록한 뒤 HTML 내용이 담긴 .txt 파일을 업로드합니다.

  2. 권한·파일 검사

    관리자 권한, 빈 파일, 허용 확장자, 원본 중복 등록 여부를 확인합니다.

  3. Python으로 요소 추출

    표준 입력으로 받은 HTML에서 태그·내용·순서를 추출해 JSON으로 반환합니다.

  4. 원본 문단 저장

    결과를 OriginDocument로 저장하고, 이후 작성되는 번역을 해당 문단에 연결합니다.

  • 번역 단위 추출BeautifulSoup로 p, ul, ol, h1~h4, code 요소를 추출하고 순서인 pOrder와 내용을 함께 반환합니다.
  • 프로세스 간 데이터 전달Spring Boot가 ProcessBuilder로 Python을 실행하고 파일 내용을 표준 입력으로 전달한 뒤, 반환된 JSON을 역직렬화합니다.
  • 처리 실패 구분Python의 종료 코드와 결과 변환 실패를 확인해 원본 등록 오류로 처리합니다.

사용자는 문서 전체를 번역하지 않고 원하는 문단부터 참여할 수 있고, 서비스는 문단 단위의 기여를 원문 순서대로 제공할 수 있습니다. 지정한 HTML 태그를 추출하는 방식으로 구현해, 처리할 문서의 구조에 따라 파서 보완이 필요할 수 있습니다.

문단별 베스트 번역과 좋아요 조회

같은 문단에 여러 번역이 쌓이면 기본으로 보여줄 번역을 선택할 기준이 필요합니다. 선택한 번역은 원문 순서를 따라야 하고, 화면에서 사용할 좋아요 정보도 함께 제공해야 했습니다.

번역 후보에서 문서 전체의 읽기 흐름을 만드는 과정
  1. 후보 조회·정렬

    공개 상태의 번역을 대상으로 좋아요를 집계하고 문단 ID, 좋아요 수, 작성 시각 기준으로 정렬합니다.

  2. 문단별 번역 선택

    정렬된 결과에서 문단마다 첫 번역 ID를 선택합니다.

  3. 원문 순서로 배치

    선택한 번역을 조회하고 원본 문단의 pOrder 순서로 배치합니다.

  4. 좋아요 정보 결합

    번역 ID 목록으로 좋아요 사용자 목록을 일괄 조회하고 ID별로 묶어 응답에 넣습니다.

QueryDSL 조회 결과를 Java에서 문단별로 선별하는 방식으로 구현했습니다. 좋아요 수가 많은 번역을 우선하고, 같은 경우에는 작성 시각이 최신인 순서로 선택합니다. 베스트 번역 조회의 초기 구현을 담당했고, 정렬 조건에는 이후 팀원의 수정도 반영됐습니다.

좋아요 정보는 응답에 포함할 번역 ID를 모아 IN 조건으로 조회했습니다. 조회한 사용자 목록을 번역 ID별로 묶어 좋아요 수와 함께 응답에 결합했습니다. 프런트엔드에서는 별도로 원문을 IndexedDB에 저장해 재조회에 활용했습니다.

SSE 연결과 DB 연결의 수명

문제: 오래 유지되는 구독 요청이 DB 연결을 점유했습니다.

번역 좋아요나 게시글·댓글·문의의 답변을 실시간으로 알리기 위해 SSE를 사용했습니다. 구현 과정에서 구독이 불필요하게 DB 연결을 점유하고, 새로고침으로 재연결이 반복되면서 연결 풀이 부족해지는 문제를 겪었습니다.

  • 알림 기능 구현알림 이벤트의 생성·저장·전송과 SSE 구독 로직을 구현했습니다. 저장된 알림은 목록 조회, 읽음 처리, 삭제할 수 있도록 했습니다.
  • 내가 수정한 부분문제 해결 과정에서 구독 메서드의 @Transactional을 제거했습니다.
  • 팀원들과 완성한 수정BE 팀원들의 도움으로 원인을 확인하고, 사용자 엔티티를 다시 조회하는 대신 인증 객체에서 사용자 ID를 읽도록 구독 경로를 수정했습니다.
구독에 필요한 사용자 식별과 알림 저장·전송의 역할
  1. 구독·사용자 식별

    SecurityContextHolder의 인증 객체에서 사용자 ID를 읽고 SSE 연결을 관리합니다.

  2. 활동 발생·알림 저장

    번역 좋아요, 게시글 댓글, 대댓글, 문의 답변에 대한 알림을 DB에 저장합니다.

  3. 실시간 이벤트 전송

    해당 사용자의 SSE가 연결돼 있으면 알림 이벤트를 보냅니다.

  4. 저장된 알림 조회

    사용자는 알림 목록을 조회하고 읽음 처리하거나 삭제할 수 있습니다.

구독을 유지하기 위한 사용자 식별과 알림 데이터를 저장·조회하는 작업을 구분했습니다. 오래 유지되는 요청에서는 기능의 동작뿐 아니라 어떤 자원을 얼마나 오래 점유하는지도 함께 살펴야 한다는 점을 배웠습니다.

개인 메모·예외 처리와 테스트

문서를 읽으며 남길 개인 메모의 생성·조회·수정·삭제 API를 구현했습니다. 로그인한 사용자를 확인하고, 개별 메모를 조회·수정·삭제할 때는 메모 ID와 사용자 ID를 함께 사용했습니다.

  • 입력과 권한 확인문서·번역·알림·메모에서 필요한 입력이 없거나 리소스를 찾지 못하거나 권한이 없는 경우를 구분해 예외 처리했습니다.
  • 문서·번역 테스트문서와 번역의 조회·작성·좋아요에 관한 서비스·컨트롤러 테스트 코드를 작성했습니다.
  • 알림 테스트알림 조회·삭제·생성에 관한 테스트를 작성했습니다. 생성 테스트는 주로 DB 저장 여부를 확인합니다.

문서화와 함께 문제를 푼 경험

  • README·소개 영상README 초안과 소개 영상을 제작해 서비스의 기능과 사용 흐름, 팀의 담당 범위를 정리했습니다.
  • 시연 데이터 준비공식 문서와 게시글·번역 데이터를 준비해 공동 번역의 흐름을 시연할 수 있도록 했습니다.
  • BE 팀원들과 문제 해결SSE 연결 문제를 공유하고 구현을 함께 점검했습니다. 직접 작성한 기능을 끝까지 확인하면서도 혼자 원인을 찾기 어려운 상황에서는 도움을 받아 수정했습니다.

문단별 번역이라는 서비스 흐름을 데이터 관계와 조회 API로 옮기고, 실시간 알림이 사용하는 자원까지 살펴본 프로젝트입니다. 담당 기능을 다른 팀원의 화면·인프라와 연결하고 문제를 함께 해결하는 경험을 쌓았습니다.