프론트엔드를 붙이고 나니 기분이 꽤 좋았다.
사진을 선택하고,
분석 버튼을 누르면,
Spring Boot API가 호출되고,
분석 결과가 화면에 나타났다.
백엔드에서 JSON으로만 확인하던 기능이 실제 화면에서 동작했다.
그런데 서비스를 직접 사용해보면서 생각이 달라졌다.
기능이 동작하는 것과 서비스가 제대로 동작하는 것은 달랐다.
프론트엔드는 단순히 화면을 예쁘게 보여주는 역할만 하는 것이 아니었다.
오히려 지금까지 숨어 있던 문제를 찾아주는 테스트 도구에 가까웠다.

API 테스트에서는 문제가 없어 보였다
백엔드를 개발할 때는 주로 API의 응답을 확인했다.
예를 들어 분석 요청을 보내면 이런 결과가 내려왔다.
{
"riskScore": 82,
"riskLevel": "CRITICAL",
"detectedItems": [
{
"type": "PHONE_NUMBER",
"value": "010-****-5678"
}
]
}
응답도 정상이고,
상태 코드도 정상이고,
원하는 데이터도 들어 있다.
그러면 자연스럽게 생각하게 된다.
잘 되고 있네.
그런데 실제 화면에 이 데이터를 뿌려보니 질문이 하나씩 생기기 시작했다.
첫 번째 문제
"개인정보가 발견된 건 알겠는데 어디에 있는 거지?"
화면에는 이런 결과가 표시됐다.
위험도 : HIGH
탐지된 개인정보
- 전화번호
- 이메일
- 이름
개발자인 나는 이 결과를 이해할 수 있었다.
하지만 사용자의 입장에서 보면 부족했다.
사진에 전화번호가 있다고 알려주기는 하지만,
사진의 어느 부분에 전화번호가 있는지는 알 수 없었다.
예를 들어 사진 한 장에 간판, 명함, 노트북 화면, 문서가 함께 찍혀 있다면 사용자는 결국 사진 전체를 다시 확인해야 한다.
그 순간 서비스의 핵심 기능을 다시 생각하게 됐다.
내가 만들고 싶은 것은 단순히
"개인정보가 있습니다."
라고 알려주는 프로그램이 아니었다.
궁극적으로는
"여기에 개인정보가 있으니 이 부분을 확인하세요."
라고 알려줘야 했다.
그래서 OCR 결과에서 텍스트만 가져오는 것으로 끝내지 않고 텍스트의 위치 정보까지 활용해야 한다는 결론을 내렸다.
OCR 결과에는 이미 답이 있었다
OCR은 단순히 글자만 반환하는 것이 아니었다.
텍스트가 이미지의 어디에 존재하는지 좌표 정보도 가지고 있다.
개념적으로 보면 이런 형태다.
"010-1234-5678"
x : 420
y : 310
width : 230
height : 45
그렇다면 해야 할 일은 명확했다.
OCR
↓
텍스트 + 좌표 추출
↓
개인정보 탐지
↓
탐지된 개인정보와 OCR 텍스트 연결
↓
BoundingBox 생성
백엔드에 이미 존재하던 두 기능을 연결하면 됐다.
하나는 OCR,
다른 하나는 PII 탐지였다.
문제는 이 둘이 각각 따로 움직이고 있었다는 것이다.
기능이 존재한다고 연결되어 있는 것은 아니었다
이 부분이 이번 개발에서 꽤 재미있었다.
OCR은 정상적으로 동작했다.
PII 탐지도 정상적으로 동작했다.
둘 다 개별적으로 보면 문제가 없었다.
하지만 서비스 입장에서는 둘 사이에 연결이 없었다.
예를 들어 OCR이
010-1234-5678
이라는 문자열과 위치를 찾아냈고,
PII 분석기가
PHONE_NUMBER
라고 판단했다고 해도,
현재 구조에서는
이 전화번호가 OCR 결과의 어느 위치에서 발견된 것인가?
를 알 수 없었다.
그래서 데이터 구조를 다시 바라보기 시작했다.
BoundingBox를 추가했다
결국 탐지 결과에 위치 정보를 함께 전달하도록 구조를 확장했다.
개념적으로는 이런 형태다.
{
"type": "PHONE_NUMBER",
"value": "010-****-5678",
"confidence": 0.99,
"boundingBox": {
"x": 420,
"y": 310,
"width": 230,
"height": 45
}
}
이 데이터가 있으면 프론트엔드에서 할 수 있는 일이 크게 늘어난다.
사진 위에 빨간 사각형을 그릴 수도 있고,
위험 영역을 강조할 수도 있고,
나중에는 해당 영역만 자동으로 블러 처리하는 기능도 만들 수 있다.
단순히 데이터 하나를 추가한 것이지만,
서비스의 방향이 훨씬 명확해졌다.
두 번째 문제
실제 데이터는 내가 예상한 것처럼 깔끔하지 않았다
개발할 때 가장 편한 데이터는 내가 예상한 데이터다.
전화번호 → 전화번호답게 들어온다.
이메일 → 이메일답게 들어온다.
주소 → 주소답게 들어온다.
하지만 실제 이미지는 그렇지 않았다.
OCR은 사진의 해상도나 각도,
글자의 크기,
배경,
빛,
글자 간격 등에 따라 결과가 달라질 수 있다.
예를 들어 사람이 보기에는
010-1234-5678
인데 OCR 결과에서는 일부 문자가 잘못 인식될 수도 있다.
그런데 개인정보 탐지 로직에서는 문자열이 달라지면 탐지 결과까지 달라질 수 있다.
그때 다시 느꼈다.
테스트용 데이터에서 잘 되는 것과 실제 사진에서 잘 되는 것은 완전히 다른 문제였다.
그래서 직접 여러 형태의 이미지를 넣어보면서 결과를 확인하기 시작했다.
세 번째 문제
결과가 없을 때도 화면은 정상적이어야 했다
개발자는 성공한 경우를 먼저 만든다.
나 역시 처음에는
사진 업로드
→ 분석 성공
→ 개인정보 탐지
→ 결과 출력
이라는 정상 흐름을 먼저 생각했다.
하지만 실제 사용자는 항상 정상적인 사진만 올리지 않는다.
개인정보가 없는 사진도 있고,
메타데이터가 없는 이미지도 있고,
지원하지 않는 파일을 선택할 수도 있다.
분석 중 오류가 발생할 수도 있다.
그래서 화면에도 각각의 상태가 필요했다.
분석 전
↓
분석 중
↓
분석 성공
또는
↓
탐지 결과 없음
또는
↓
분석 실패
이 상태를 구분하지 않으면 사용자는 버튼을 눌렀는데 분석 중인지,
오류가 난 것인지,
정말 아무것도 발견되지 않은 것인지 알 수 없다.
작은 차이지만 사용성에서는 꽤 큰 차이였다.
프론트엔드가 백엔드를 다시 설계하게 만들었다
처음에는 이렇게 생각했다.
백엔드 기능은 거의 끝났으니 이제 화면만 붙이면 된다.
그런데 실제로는 반대였다.
화면을 만들고 직접 서비스를 사용해보니 백엔드에서 부족한 데이터가 보였고,
API 구조를 다시 보게 됐고,
예외 상황도 생각하게 됐다.
결국 이런 흐름이 반복됐다.
백엔드 구현
↓
프론트엔드 연결
↓
직접 사용
↓
문제 발견
↓
백엔드 수정
↓
다시 프론트엔드 테스트
예전에는 이것을 번거로운 수정 작업이라고 생각했을 수도 있다.
지금은 조금 다르게 느껴진다.
이 과정 자체가 서비스를 만드는 과정이었다.
AI도 문제 해결 과정에서 계속 사용했다
이번에도 ChatGPT와 Claude를 적극적으로 사용했다.
다만 단순히
코드를 만들어줘.
라고 요청하는 방식보다는 문제를 함께 쪼개는 데 많이 사용했다.
예를 들어
OCR에서는 좌표가 나오고 있는데
현재 개인정보 탐지 결과에는 좌표가 없다.
두 데이터를 어떤 기준으로 연결하는 것이 좋을까?
같이 현재 구조와 문제를 설명했다.
AI가 제안한 방법을 그대로 사용하는 것이 아니라,
현재 DTO 구조와 기존 코드에 맞는지 확인하고,
필요한 부분만 가져와 적용했다.
특히 익숙하지 않은 프론트엔드 영역에서는 브라우저에서 어떤 데이터를 어떻게 표현해야 하는지 빠르게 확인하는 데 도움이 됐다.
결국 중요한 것은 질문이었다.
문제를 정확하게 설명할수록 AI가 주는 답도 훨씬 구체적이었다.
직접 써봐야 문제가 보인다
이번에 가장 크게 배운 것은 이것이다.
코드를 보고 있으면 기능이 보인다.
API를 테스트하면 데이터가 보인다.
하지만 화면에서 직접 사용해봐야 사용자의 문제가 보인다.
전화번호를 탐지하는 기능을 만드는 것과
사용자에게 전화번호가 어디 있는지 알려주는 것은 다른 문제였다.
분석 결과를 반환하는 것과
사용자가 결과를 이해할 수 있게 만드는 것도 다른 문제였다.
프론트엔드를 붙인 덕분에 오히려 백엔드의 부족한 부분을 발견할 수 있었다.
그리고 하나씩 연결하면서 서비스의 모습도 조금 더 선명해지고 있다.
지금의 목표는 단순하다.
사진을 올린다.
↓
위험 요소를 찾는다.
↓
어디가 위험한지 보여준다.
↓
필요하다면 그 부분을 가린다.
처음에는 이미지 분석 API 하나에서 시작했다.
그런데 직접 사용해보고,
부족한 부분을 찾고,
다시 고치다 보니 조금씩 내가 처음 생각했던 서비스에 가까워지고 있다.
기능을 만드는 것도 중요하지만
직접 사용해보는 것이 더 중요할 때가 있다.
이번에는 프론트엔드가 그 사실을 알려줬다.
'RE:개발자' 카테고리의 다른 글
| 기능이 된다고 제품이 되는 건 아니었다 | RE:개발자 #9 (0) | 2026.09.20 |
|---|---|
| RE:개발자 — 다시 개발자가 되어가는 기록 | 전체 목차 (0) | 2026.09.20 |
| 프론트엔드를 붙였더니, 드디어 서비스처럼 보이기 시작했다 | RE:개발자 #7 (0) | 2026.09.19 |
| OCR로 개인정보를 찾기 시작했다, 하드코딩을 걷어낸 날 | RE:개발자 #6 (0) | 2026.09.18 |
| Spring Boot로 REST API를 만들었는데, 아직 아무것도 분석하지 않았다 | RE:개발자 #5 (0) | 2026.09.17 |