DevTI
RUNTIME

보안 · AI

사용자 요청이 지나는 길과 그 길을 지키는 보안 장치, 그리고 외부 AI 없이 직접 만든 맞춤 분석 엔진.

03 - ARCHITECTURE & SECURITY

요청 흐름과 보안 관문
  • 경로·브라우저 → Next → Security 필터 → 백엔드 → MySQL
  • 백엔드·Controller → Service → Repository
  • 로그인·로컬(닉네임+비밀번호 · BCrypt) · 구글 OAuth2(1회용 코드 → JWT 교환)
  • 남용 방어·IP 레이트리밋(가입 · 로그인 · 닉네임 확인) · 실패 메시지 통일로 계정 열거 완화
  • 관문·각 단계에 검증 · 인가 · 방어 (점선)
쉽게 말하면

사용자가 버튼을 누르면 그 요청이 화면 → 서버 → 데이터베이스까지 어떤 길을 지나는지, 그리고 그 길목마다 어떤 보안 검문소가 지키는지를 보여줍니다. 로그인 확인, 권한 확인, 잘못된 입력 차단, 무차별 시도 막기 같은 장치들이 각 단계에 붙어 있어요.

아래 그림에서 가로 실선은 요청이 흐르는 길, 거기에 점선으로 매달린 것들이 그 길을 지키는 보안 장치입니다.

인증·인가입력 검증업로드 방어

실선 = 요청·응답 흐름 / 점선 = 그 단계를 지키는 보안 장치. 각 노드의 작은 글씨는 실제 클래스 이름 - JWT(Access Only)·@Valid·CORS· 조회수 가드·업로드 검증·로그인 레이트리밋이 길목마다 배치.

04 - AI ENGINE (DPE)

결정적 맞춤화 엔진 (DPE · Deterministic Personalization Engine)
  • 엔진·카드 상세의 “맞춤 강점 분석” - 외부 LLM 미사용
  • 처리·순수 함수 6단 파이프라인
  • 저장·능력치 = 쓰기 시 영속 / 서사 = 읽기 시 재계산
쉽게 말하면

카드 상세의 ‘맞춤 강점 분석’을 ChatGPT 같은 외부 AI에 물어보지 않고, 직접 만든 규칙 엔진으로 계산합니다. 외부 AI는 부를 때마다 답이 달라지고, 돈이 들고, 없는 사실을 지어낼 수 있어서 - 대신 같은 사람이면 항상 같은 결과가 나오는 방식을 택했어요. (DPE = Deterministic Personalization Engine, 우리말로 ‘결정적 맞춤화 엔진’)

아래 그림은 설문 답이 6단계를 거쳐 최종 분석 문장이 되는 과정이고, 그 아래 표는 이 방식이 외부 AI 대비 무엇이 나은지 비교한 것입니다.

실선 = 파이프라인 / 점선 = Rule-as-Data 피드 / 아래 분기 = 이원 영속(능력치는 쓰기 때 저장, 서사는 읽을 때 재계산)

삽질의 과정
  1. 1처음 생각
    Claude API를 그냥 붙이기

    외부 AI에 물어보면 끝일 줄 알고 시작.

  2. 2막힌 벽 ①
    답이 매번 다름

    같은 설문인데 문장이 바뀜. 공유·재조회 때 신뢰가 안 감.

  3. 3막힌 벽 ②
    배포 시 토큰 비용 부담

    실제 서비스가 목표인데, 사용자 늘수록 호출마다 비용 녹는 게 걱정됨.

  4. 4피벗
    AI 구조를 흉내내기

    설문을 카테고리별로 잘게·정량적으로 받고, RAG·벡터DB가 하는 일을 규칙으로 대신.

  5. 5구현으로 번역
    진짜 개념 → 가벼운 버전

    임베딩→손수 만든 특성 숫자, 벡터DB 검색(retrieval)→규칙 매칭, 확률 샘플링→결정적 선택.

  6. 6결론
    서비스에 맞춘 흉내낸 버전

    진짜 벡터DB·RAG면 더 매끄럽지만, 비용 0·항상 같은 결과 위해 가볍게 재현해 직접 설계.

① 설문 5갈래가 능력치가 되기까지

방문자가 고른 답 하나하나가 숫자 기여로 쌓여 5개 능력치 점수가 됩니다. 먼저 어떤 질문이 무엇을 재는지 볼게요.

설문 갈래무엇을 재나특징
직군큰 방향의 기준선적성검사가 정한 UI디자이너·퍼블리셔·프론트·백엔드
스킬셋+숙련도실제로 쥔 무기Figma·React·Spring… × 입문/능숙/전문
관심분야숨은 성향'보기 좋게 꾸미기'처럼 직군을 안 드러내는 일상어(최대 2개)
목표·성향일하는 결몰입/협업 · 빠른실행/신중설계 · 넓게/한우물
MBTI타고난 기질16타입을 E/N/T/J 4개 스위치(0/1)로 분해
문항을 일부러 '가린' 이유 - 자기충족 답변 막기
사람은 검사를 받을 때 실제 성향이 아니라 되고 싶은 직군에 맞는 답을 무의식적으로 고르기 쉬워요. 그래서 관심·성향 문항을 “디자이너가 되고 싶다” 같은 말이 아니라 “보기 좋게 꾸미기”처럼 결과가 안 비치는 일상어로 감췄습니다. 어느 답이 어느 직군으로 이어지는지 화면에선 예측할 수 없게 해, 결과가 ‘내가 맞춘 것’이 아니라 ‘발견’이 되도록요.
한 능력치가 계산되는 실제 예 - '디자인' 점수
디자인 = 50(기본) + 직군 가산 + 디자인스킬 수 × 4 + 관심‘표현’ +8 + 직관(N) +4
각 항이 눈에 보이는 덧셈이라, 결과가 왜 이 점수인지 그대로 되짚을 수 있어요. 블랙박스인 외부 AI와 정반대 - 설명 가능(explainable)합니다.
② 핵심 마법 - 왜 랜덤이 아니라 '해시'인가

조건에 맞는 문구 후보가 여럿일 때 주사위를 굴리지 않습니다. 프로필 전체를 정렬해 만든 고정 지문(해시)으로 고르죠. 그래서 같은 사람은 몇 번을 봐도 같은 분석이 나옵니다.

왜 '항상 같은 결과'가 중요한가
공유하거나 자랑할 때 볼 때마다 결과가 바뀌면 신뢰가 깨집니다. 결정적이라 결과를 DB에 저장할 필요도 없어요 - 볼 때마다 즉시 다시 계산해도 늘 같으니까.
정교한 문구가 항상 먼저
우선순위(weight)가 절대 우선이라, 직군·기질·스킬을 콕 집은 구체 문구가 일반 문구보다 먼저 뽑힙니다. 해시는 같은 우선순위끼리만 순서를 돌려 다양성을 줍니다.
③ 직군 적합도를 '확률처럼' 보여주기

적성검사가 정한 주·부 직군을 기준선으로, 4개 직군에 점수를 매기고 그 점수를 전체에서 차지하는 비율(%)로 바꿔 보여줍니다. 분류 모델이 마지막에 점수를 확률로 바꾸는 것과 같은 발상인데, 외부 모델 없이 간단한 산수로 했어요.

백엔드
52%
프론트엔드
27%
퍼블리셔
13%
UI 디자이너
8%

예시 분포 - 성향·관심에 따라 사람마다 막대 모양이 달라져요. 같은 입력이면 늘 같은 비율이라 결과가 흔들리지 않습니다.

LLM 호출과 비교
LLM API 호출
DPE (이 프로젝트)
일관성
호출마다 다름
같은 입력 = 같은 출력
신뢰성
환각 가능
검증된 문구만 출력
비용·지연
호출당 과금 · 수초
0원 · ms 단위
설명가능성
블랙박스
왜 이 결과인지 추적
설계 의도
부패방지 경계 (Ports & Adapters)
엔진은 JPA를 모르는 순수 계층. 카드 서비스가 toAnalysisRequest()로 순수 record를 전달. DB·프레임워크 결합을 한 곳에 가둬 테스트·재사용이 쉬운 구조.
Rule-as-Data
문구를 코드가 아니라 fragments.json 으로 분리. 엔진 수정 없이 콘텐츠 추가·교체·A/B 테스트 가능.
결정적 vs 확률적
LLM은 확률적이라 매번 다른 결과. DPE는 결정적이라 저장 없이도 안 바뀌는 공유 결과. 규칙으로 충분한 영역과 LLM이 필요한 영역의 구분.