사내보안 규정 · 감사 로그 분석 AI 에이전트

권한이 없는 문서는
“없다”가 아니라
애초에 검색되지 않는다.

같은 질문을 던져도 부서와 등급에 따라 근거 조항이 달라집니다. 권한 검사가 검색보다 먼저 일어나기 때문에, 가려진 문서는 결과 개수로도 순위의 빈자리로도 존재를 드러내지 않습니다.

데모 백엔드는 상시 가동이 아닙니다 — 24시간 켜두지 않는 무료 구성이라, 꺼져 있으면 화면이 그 사실을 명시합니다.

01 · 프로젝트 개요

규정 검색은 “찾아주는 것”이
아니라 “못 찾게 하는 것”이 어렵다.

사내 규정과 감사 로그는 누구나 다 볼 수 있는 자료가 아닙니다. 인사 징계 기준, 임원 성과급 산정, 특정 서버의 접속 기록 — 읽을 수 있는 사람이 정해져 있습니다.

문제

사후 필터링은 샌다

일반적인 RAG 는 유사도 상위 k 건을 먼저 뽑고 그 다음 권한으로 거릅니다. 그러면 결과 개수 자체가 신호가 됩니다 — “10건 요청했는데 3건만 왔다”는 사실이 권한 밖에 7건이 있다는 것을 알려줍니다.

해법

권한을 검색보다 먼저

권한 조건을 ORDER BY·LIMIT 보다 앞선 WHERE 절에 둡니다. 볼 수 있는 문서 안에서만 상위 k 를 고르므로, 허용된 조항이 k 건 이상이면 누가 물어도 개수가 같습니다.

검증

새면 반드시 터지는 테스트

“권한 검사를 했다”가 아니라 “사후 필터링으로 되돌리면 실패한다”를 테스트로 못 박았습니다. 의도적으로 새는 경로를 코드베이스 안에 두고, 그것이 실제로 새는 것을 테스트가 확인합니다.

419
자동화 테스트
백엔드 381 · 프론트엔드 38
170커밋
5일간
2026-08-31 ~ 09-04
13.1k줄
애플리케이션 코드
Python 8.8k · TypeScript 3.9k · CSS 0.4k
109
개발 로그
커밋마다 무엇을 왜 했는지 기록

차별성

“권한은 사다리가 아니라 격자다”를
화면에서 증명합니다.

등급이 높을수록 많이 보는 것이 아닙니다. 부서와 등급 두 축이 동시에 걸리기 때문에, 최고 등급이면서도 못 보는 문서가 존재합니다. 이 프로젝트는 그것을 말로 설명하지 않고 실측값으로 보여줍니다.

질의김개발개발팀 · 1 한보안정보보안팀 · 2 최임원경영지원팀 · 3 무엇이 갈랐나
비밀번호는 얼마나 자주 바꿔야 하나 동일 동일 동일 아무것도열 계정 전부 같은 조항 집합
운영 서버 접속에 어떤 승인이 필요한가 5.1.1 수신 2.6.1 2.6.1 부서개발팀만 개발팀 전용 규정에 닿는다
임원 성과급은 어떤 기준으로 정해지나 2.2.x 2.2.x 6.1.x 수신 등급등급 3 두 명만 임원 문서에 닿는다
침해사고 접수와 보고 기한 미수신 5.3.1 · 5.3.3 미수신 둘 다최고 등급도 부서가 다르면 못 본다

2026-09-03 실측 · 하이브리드 검색 k=10 · 비교 대상은 clause_code 집합. 마지막 행이 이 프로젝트의 논지입니다 — 최임원은 등급 3이지만 정보보안팀 문서를 받지 못합니다.

02 · 시스템 구성

세 계층, 한 방향 의존.

core/ 는 바깥 계층이나 프레임워크를 import 하지 않습니다. 그 규칙은 문서가 아니라 CI 에서 강제됩니다 — 어기면 빌드가 실패합니다.

core/

순수 도메인

권한 판정 visible(), 하이브리드 검색 융합(RRF), 에이전트 정책. DB·HTTP·LLM 을 모릅니다. 포트(ports.py)로만 바깥과 말합니다.

adapters/

바깥 세계

pgvector 검색, 로그 검색, 임베딩 모델, Gemini 호출. 포트를 구현할 뿐 도메인 규칙을 갖지 않습니다.

api/

조립과 노출

FastAPI 라우터. 의존성 주입으로 core 와 adapters 를 엮습니다. 요청 스키마에 department·clearance없습니다.

요청 한 번이 지나가는 길

브라우저
Google 로그인 세션
Vercel BFF
페르소나를 쿠키에서 읽음
Cloudflare Tunnel
공유 시크릿 검증
FastAPI
이름 → 등급 번역
pgvector
권한 WHERE + 상위 k
Gemini
근거 조항으로만 답변

브라우저가 닿는 것은 Vercel 의 라우트 핸들러 하나뿐입니다. 백엔드 주소도 공유 시크릿도 브라우저로 내려가지 않고, 권한 값은 요청 본문에 실리지 않습니다 — 서버가 페르소나 이름을 principals 테이블에서 등급으로 번역합니다. 클라이언트가 자기 등급을 보낼 수 있으면 그것이 곧 사칭 경로이기 때문입니다.

화면

직원 · 설명 · 관리자,
세 개의 면.

방문자는 허브에서 직원 한 명을 고르고 그 사람이 되어 서비스를 씁니다. 직원 면에는 페르소나 선택기가 없습니다 — 내가 그 사람이기 때문입니다. 비교하는 도구는 설명 면과 관리자 면에만 둡니다.

실행 중인 애플리케이션을 그대로 캡처했습니다. 한 곳만 손댔습니다 — 로그인 계정 자리의 메일 주소를 demo@example.com 으로 바꾸고 찍었습니다. 그 밖의 숫자와 문장은 전부 실제 화면의 것입니다.

허브 — 누구로 둘러볼지 고른다

허브 — 누구로 둘러볼지 고른다

열 명의 시연 계정이 부서 4개 × 등급으로 격자를 이룹니다. 지금 보는 중인 계정은 카드에 표시됩니다.

/
질의 — 근거 조항과 함께 답한다

질의 — 근거 조항과 함께 답한다

김개발(개발팀·등급 1)이 운영 서버 접속을 묻자 개발팀 전용 규정 5.1.1 이 근거로 붙었습니다. 요구 등급은 1 이라 누구나 넘지만 부서가 다르면 등급 3 계정에도 오지 않습니다.

/ask
내 문서 — 내가 볼 수 있는 것만

내 문서 — 내가 볼 수 있는 것만

목록에 없는 문서는 제목조차 브라우저로 오지 않습니다. 화면은 이 목록을 고른 코드가 실제 강제 지점(SQL)의 사본이라는 사실까지 적어둡니다.

/my/documents
문서 가시성 — 누가 무엇을 보는가

문서 가시성 — 누가 무엇을 보는가

계정을 바꿔가며 같은 코퍼스가 어떻게 갈리는지 봅니다. 허용 부서가 비어 있으면 전사 공개라는 규칙까지 화면에 적혀 있습니다.

/documents
동작 원리 — 새는 것을 실행해 보인다

동작 원리 — 새는 것을 실행해 보인다

사전 필터링과 순진한 사후 필터링을 그 자리에서 여섯 번 검색해 비교합니다. LLM 을 부르지 않아 질의 횟수를 쓰지 않습니다.

/how
감사 로그 — 호스트 단위 권한

감사 로그 — 호스트 단위 권한

권한은 호스트에 걸립니다. 같은 호스트 안에 다른 사람의 기록이 섞여 있을 수 있다는 사실을 화면이 먼저 말합니다.

/logs
관리자 대시보드 — 실제 요청만

관리자 대시보드 — 실제 요청만

시연용으로 채운 데이터가 아니라 이 배포에서 실제로 일어난 요청만 보여줍니다. 표본이 적으면 적다고 적습니다.

/admin

기술 스택

무엇을 어디에 썼는가.

고른 이유와 그 대가를 함께 적습니다. 포트폴리오 운영 비용을 0원으로 유지하는 것이 이번 배포의 제약이었습니다.

검색 · 데이터

기술쓰인 곳고른 이유와 대가
Postgres 16 + pgvector 벡터 저장과 권한 필터를 한 트랜잭션에서 별도 벡터 DB 를 두면 권한 조건과 유사도 검색이 두 시스템으로 갈라진다. 한 곳에 두어야 WHERE 를 ORDER BY 앞에 놓을 수 있다.
multilingual-e5-small 한국어 규정 문서 임베딩 (384차원) 로컬에서 돌아 API 비용이 0원이다. 첫 요청에만 모델 로드로 수십 초가 걸린다 — 화면이 그 사실을 알린다.
RRF (Reciprocal Rank Fusion) 벡터 + 키워드 하이브리드 융합 조항 번호처럼 정확히 일치해야 하는 토큰은 벡터만으로 못 잡는다. 점수 스케일이 다른 두 랭킹을 순위로만 합친다.
pypdf ISMS-P 안내서 255쪽을 조항 단위로 분할 문서 전체를 통으로 넣으면 근거를 조항 번호로 지목할 수 없다.

에이전트 · LLM

기술쓰인 곳고른 이유와 대가
LangGraph 도구 호출 루프 (search_policy · query_logs) 에이전트가 규정 본문을 직접 갖지 않고 도구로만 접근한다 — 도구가 권한을 걸므로 우회할 표면이 없다.
Gemini 3.7 Flash 근거 조항 기반 답변 생성 무료 티어 안에서 쓸 수 있고 한국어 규정 문서에 충분하다. 하루 질의 상한을 프론트에서 건다.
AccessViolation 정책 재검증 실패 시 예외 예외 메시지에 식별자만 담고 본문·제목을 담지 않는다 — 오류 메시지가 우회 경로가 되지 않게.

프론트엔드

기술쓰인 곳고른 이유와 대가
Next.js 16 (App Router) 화면 8개 + BFF 서버 컴포넌트에서 쿠키를 읽어 페르소나를 판정한다. 브라우저는 백엔드를 직접 부르지 않는다.
Auth.js v5 (Google OAuth) 로그인 비밀번호를 직접 다루지 않는다. 관리자 여부는 이메일 알리스트로 판정한다.
TypeScript 7 lib/ 순수 함수 + 타입 권한 판정·조사 처리 같은 순수 로직을 node --test 로 DOM 없이 검증한다.
CSS 단일 파일 디자인 시스템 프레임워크 없이 토큰과 컴포넌트 클래스만. 900px 한 갈래의 미디어 쿼리로 모바일을 접는다.

인프라 · 배포

기술쓰인 곳고른 이유와 대가
Vercel 프론트엔드 (상시) 무료 티어. 도메인 whoareryu.cloud 를 여기에 붙였다.
Cloudflare Tunnel 로컬 백엔드 공개 고정 IP 도 포트 개방도 없이 내 맥북을 공개한다. 24시간 가동하지 않는다는 것이 그 대가다. 클러스터 안에서 도는 전용 터널이라 백엔드가 남의 스택과 네트워크를 공유하지 않는다.
k3s · Colima 백엔드 · 터널 맥북 위의 단일 노드 온프레미스. 이미지를 Colima 의 도커 데몬에서 빌드해 레지스트리 없이 배포한다 — k3s 가 같은 VM 의 그 데몬을 CRI 로 쓴다.
Docker Compose Postgres (pgvector) DB 만 맡는다. 클러스터에 들이지 않아 열람 기록이 볼륨에 그대로 남고 클러스터는 무상태다.

검증 도구

기술쓰인 곳고른 이유와 대가
pytest (마커 분리) 351건 llm·db·model·corpus 를 기본 제외한다 — 컨테이너나 API 키가 없는 환경에서도 기본 스위트가 돈다.
ruff 린트 + 포맷 line-length 100 · E

F

기술쓰인 곳고른 이유와 대가

I

기술쓰인 곳고른 이유와 대가

UP

기술쓰인 곳고른 이유와 대가

B.

기술쓰인 곳고른 이유와 대가
node --test 30건 프론트엔드 순수 함수. 별도 러너를 설치하지 않는다.
계층 경계 테스트 import 방향 강제 core/ 가 adapters 를 import 하면 실패한다. 규칙을 주석이 아니라 테스트가 지킨다.

03 · 개발 원칙

코드를 읽어 결함을 찾는 대신,
결함이 스스로 드러나게 만든다.

저는 빠르게 만들고 여러 번 검증해서 마무리합니다. 속도를 택하면 보통 안전성을 잃는다고들 합니다. 그래서 보안을 주제로 잡았습니다 — 취약점이 치명적인 도메인에서 속도를 유지하면서 신뢰할 수 있는 결과를 내는 방법을 찾아보려고요.

AI 도구는 코드를 사람이 읽는 속도보다 빠르게 만듭니다.
그래서 모든 라인을 눈으로 좇는 대신, 틀리면 반드시 터지는 장치를 먼저 세웁니다.
원칙 1

화면에 뒷받침 없는 문장을 한 줄도 두지 않는다

제품 화면의 모든 주장은 코드·스키마·실측 중 하나를 출처로 갖습니다. “세 계정이 같은 조항을 받는다”라고 적으려면 먼저 재봐야 하고, 계정이 열 명이 되면 그 문장은 다시 재서 고쳐야 합니다.

실제로 이 규칙이 화면의 거짓 문장 넷을 잡았습니다 — 전부 “파일을 인용했지만 그 파일이 그렇게 말하는지 확인하지 않은” 형태였습니다.

원칙 2

고쳤다는 말 대신 못 박는 테스트

누출을 막았다고 적는 대신, 의도적으로 새는 경로를 코드베이스에 남겨두고 그것이 실제로 샌다는 것을 테스트로 확인합니다. 방어가 무너지면 그 테스트가 먼저 터집니다.

새는 경로는 demo/ 한 곳에 격리하고, 그 디렉토리를 import 할 수 있는 파일 목록을 집합 동일성으로 고정합니다 — 셋째 파일이 생기면 테스트가 먼저 터집니다.

원칙 3

모른다를 없다로 적지 않는다

로딩 중이거나 백엔드가 꺼져 있을 때 “0건”이라고 쓰면, 사용자는 확인된 사실로 읽습니다. 보안 알림 패널에서는 특히 그렇습니다. 실패·미확인·0 은 화면에서 서로 다른 상태여야 합니다.

원칙 4

같은 규칙의 사본은 계층마다 하나까지

권한 규칙은 SQL(사전 필터링)·Python(재검증)·TypeScript(화면 표시) 세 계층에 의도적으로 삽니다. 각자 다른 일을 하기 때문입니다. 문제가 되는 것은 같은 계층 안에서 사본이 늘어나는 쪽이고, 그건 테스트가 셉니다.

04 · 개발 일정 및 진행 현황

계획한 여섯 단계 중
여섯 단계를 마쳤습니다.

2026년 8월 31일 착수 · 3~4주 계획. 4일차인 오늘 기준 코어 기능과 배포가 끝났고, 남은 기간은 확장과 다듬기에 씁니다.

코어 마일스톤 W1–W6 6 / 6 완료 · 확장 단계 진행 중
착수 2026-08-31 현재 2026-09-03 (4일차) 계획 ~9월 말
2026-08-31 · W1

문서 파이프라인과 하이브리드 검색 완료

ISMS-P 인증기준 안내서 255쪽을 조항 단위로 분할해 pgvector 에 적재. 벡터·키워드 하이브리드 검색을 RRF 로 융합. 계층 경계 테스트를 CI 에 걸어 core/ 의 의존 방향을 처음부터 강제.

2026-09-01 · W2

Access Control · 누출 테스트 · 평가 하네스 완료

권한이 실제로 갈리는 합성 규정 코퍼스를 세우고, 사후 필터링이면 반드시 실패하는 누출 테스트를 작성. 검색 품질을 숫자로 재는 평가 하네스 구축.

2026-09-01 · W3 · W3b

에이전트 · API · 프론트엔드 전체 화면 완료

LangGraph 도구 호출 루프, FastAPI 엔드포인트, Google 로그인 뒤의 Next.js 화면 8개와 관리자 대시보드. 목 데이터를 화면에 두지 않는다는 규칙을 이때 세움 — 지어낸 값 대신 진짜 데이터를 만들거나 없다는 사실을 명시.

2026-09-01 · W4a · W4b

로그 파이프라인 · 동작 원리 화면 완료

운영 로그에 호스트 기준 권한을 걸고 query_logs 도구를 추가. 부분 집계를 전체로 오해시키지 않도록 무조건적 범위 고지를 붙임. 순진한 RAG 가 새는 것을 화면에서 실행해 보이는 /how 구현.

2026-09-02 · W5

배포 — whoareryu.cloud 완료

Vercel + Cloudflare Tunnel + 로컬 Docker 구성으로 무료 티어 안에서 공개. 도메인을 이전 프로젝트에서 이 프로젝트로 이전. 공개 저장소에 시크릿이 들어 있던 것을 이때 발견해 회전하고, 설정을 fail-closed 로 바꿈.

2026-09-02 · W6

페르소나를 세션 정체성으로 완료

화면을 직원 · 설명 · 관리자 세 면으로 재구성. 방문자가 직원 한 명을 고르면 그 사람이 되어 쓰고, 로그아웃하면 다시 고릅니다. 직원 면에서 페르소나 선택기를 걷어냄 — 내가 그 사람이므로 고를 것이 없습니다.

2026-09-03 · 오늘

모바일 대응 · 계정 10명 확장 · 결함 수정 진행 중

900px 한 갈래로 셸을 접고 좁은 화면의 넘침 세 곳을 막음. 시연 계정을 3명에서 10명으로 늘려 부서 4개 × 등급 격자를 채우고, 그 과정에서 드러난 화면 결함 일곱 개를 수정. 부서 이름이 어긋나 아무에게도 보이지 않던 문서 하나를 이때 발견.

~ 2026-09월 말 · 예정

확장 단계 계획

평가 하네스 지표 확대, 코퍼스 규모를 키운 누출 재현 실험, 문서 업로드 파이프라인, 접근 이상 탐지 규칙. 아래 확장·BM 절에 정리했습니다.

05 · 개발 리뷰

장치가 실제로 무엇을 잡았는가.

“검증 체계를 만들었다”가 아니라 “그 체계가 이것들을 잡았다”를 기록합니다. 아래는 이 프로젝트에서 실제로 걸린 결함들입니다.

치명

공개 저장소에 공유 시크릿이 커밋돼 있었다

백엔드를 공개하면서 docker-compose.yml 에 개발용 시크릿이 그대로 있었습니다. 회전하고, 설정 파일이 값 없이는 기동에 실패하도록 바꿨습니다.

치명

도커 네트워크 별칭 충돌로 라이브 서비스를 끊었다

다른 프로젝트의 네트워크에 backend 라는 같은 이름으로 붙어 기존 서비스 요청의 절반이 잘못된 컨테이너로 갔습니다. 즉시 되돌리고 서비스 이름을 분리한 뒤, 그 이유를 설정 파일에 적어뒀습니다.

설계

격리 울타리에 한 홉짜리 우회로가 있었다

새는 데모 경로를 demo/ 에 가뒀는데, from api.demo import compare 로 한 단계 건너 닿을 수 있었습니다. 탐지기가 최상위 이름만 보고 있었던 탓입니다. 두 경로를 모두 오염으로 세도록 고치고, 두 번째 진입점을 목록에 정직하게 올렸습니다.

테스트

통과하지만 아무것도 검증하지 않던 테스트

정렬 안정성 테스트가 수정 전후로 똑같이 통과했습니다 — 현재 코퍼스 규모에서는 그 현상이 애초에 나타나지 않았기 때문입니다. SQL 문자열의 절 위치를 직접 확인하는 테스트로 교체했습니다.

데이터

아무에게도 보이지 않던 문서

규정 문서 하나의 허용 부서가 보안팀 인데 계정·호스트 어디에도 그 문자열이 없었습니다. 부서 이름이 조인 키라 그 문서는 존재하지만 누구도 검색할 수 없었습니다. 계정을 10명으로 늘리며 격자를 채우다 드러났습니다.

화면

화면이 제 세션과 다른 사람을 보여줬다

문서 가시성 화면이 목록의 첫 사람 기준으로 그리면서 “현재 페르소나 기준”이라고 적고 있었습니다. 한보안으로 들어온 사람에게 김개발의 가시성을 보여주며 그것이 당신 기준이라고 말한 셈입니다.

전체 기록

무엇을 어떤 장치로 잡았는지, 테스트로는 잡을 수 없었던 것은 무엇이었는지, 이 방식의 한계는 무엇인지까지 별도 페이지에 정리했습니다.

06 · 확장성과 사업화

이 구조가 팔릴 수 있는 이유는
기능이 아니라 증거입니다.

사내 규정 챗봇은 이미 많습니다. 도입을 막는 것은 성능이 아니라 “그 봇이 못 볼 문서를 정말 못 보는지 어떻게 증명하느냐”입니다. 이 프로젝트는 그 증명을 산출물로 갖고 있습니다.

차별점 1

감사 가능한 권한 경계

권한 판정이 SQL 한 문장에 모여 있고, 그 문장이 어디에 쓰이는지를 테스트가 집합으로 고정합니다. 보안 심사에서 “어디를 보면 되느냐”에 파일 하나와 테스트 하나로 답할 수 있습니다.

차별점 2

부채널까지 다룬다

대부분의 구현은 내용 노출만 막습니다. 이 프로젝트는 결과 개수·순위· 부분 집계까지 관측 채널로 보고 각각에 장치를 답니다. 규제 산업일수록 이 차이가 커집니다.

차별점 3

문서와 로그를 한 규칙으로

규정 문서와 서버 로그가 같은 권한 판정 함수를 공유합니다. 보안팀이 실제로 다루는 두 자산을 한 제품에서 같은 정책으로 묶습니다.

확장 로드맵

  • 문서 업로드 파이프라인 — 고객사가 자기 규정을 넣으면 조항 단위 분할·권한 메타 부여까지 자동으로. 지금은 CLI 로만 적재합니다.
  • 권한 소스 연동 — 지금은 principals 테이블이 진실입니다. 사내 IdP(Okta·Azure AD)의 그룹을 그대로 부서·등급으로 번역하는 어댑터가 다음 단계입니다. 포트가 이미 분리돼 있어 core 는 바뀌지 않습니다.
  • 접근 이상 탐지 — 이미 access_records 에 누가 무엇을 언제 열람했는지 쌓고 있습니다. 여기에 규칙을 걸면 별도 수집 없이 이상 열람 알림이 됩니다.
  • 코퍼스 규모 확대 실험 — 플래너 인라인 누출은 문서당 2,000 청크 규모에서만 재현됩니다. 실제 고객 규모에서 재측정이 필요합니다.
  • 평가 하네스 지표 확대 — 현재는 검색 품질 위주입니다. 권한 준수율을 별도 지표로 분리해 회귀를 숫자로 봅니다.

사업 모델 가설

  • 1차 타깃 — 인증 준비 기업. ISMS-P·ISO 27001 인증을 준비하거나 유지하는 조직은 규정 문서가 이미 정리돼 있고, 열람 통제를 증빙해야 합니다. 이 제품의 감사 로그가 그대로 증빙 자료가 됩니다.
  • 과금 — 좌석이 아니라 코퍼스와 감사. 규정 문서 규모와 감사 보관 기간으로 나눕니다. 질의 수 과금은 “질문을 아끼게” 만들어 도입 목적과 충돌합니다.
  • 온프레미스 우선. 임베딩 모델이 로컬에서 돌고 DB 도 Postgres 하나라, 외부로 나가는 것은 LLM 호출뿐입니다. 그마저도 어댑터 교체로 사내 모델로 바꿀 수 있습니다 — 보안 조직의 첫 질문이 늘 이것입니다.
  • 확장 경로 — 규정에서 계약·인사 문서로. 권한 격자와 사전 필터링은 문서 종류를 가리지 않습니다. 같은 엔진으로 법무·인사 문서까지 넓힐 수 있습니다.

가설입니다. 고객 인터뷰로 검증한 내용이 아니라는 점을 명시합니다.

07 · 부록

기록.

커밋마다 무엇을 왜 했는지 남겼습니다. 결정의 이유와 그 대가가 함께 적혀 있습니다.

이 사이트에 대해

Jekyll + GitHub Pages 로 빌드합니다. 개발 로그는 jekyll/_logs/ 의 컬렉션이고, 커밋할 때마다 파일이 하나씩 늘어납니다 — 이 페이지의 숫자도 그 파일들을 세서 나옵니다. 화면 스크린샷은 실행 중인 애플리케이션에서 직접 캡처한 것이며, 표에 적힌 실측값은 라이브 백엔드에 질의해 얻은 값입니다.

Contact

보고 계신 것이 궁금하시면
직접 써보시는 게 가장 빠릅니다.

열 명의 직원 중 아무나 골라 같은 질문을 던져보세요. 누구로 묻느냐에 따라 근거 조항이 어떻게 갈리는지 바로 보입니다.

개발
류준 · 1인 개발
취업 포트폴리오
이메일
fbwns1234 [at] gmail [dot] com
GitHub
@whoareryu