<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>아키로그</title>
  <subtitle>프로젝트에서 마주한 작은 고민과 기술적 선택을 기록합니다.</subtitle>
  <link href="https://blog.archilog.dev/feed.xml" rel="self"/>
  <link href="https://blog.archilog.dev/"/>
  <updated>2026-10-11T22:09:38+09:00</updated>
  <id>https://blog.archilog.dev/</id>
  
  <entry>
    <title>AI 활용을 보고하면서, 업무 적용의 기준을 다시 정리했습니다</title>
    <link href="https://blog.archilog.dev/posts/defining-ai-adoption-criteria-through-reporting/"/>
    <updated>2026-10-06T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/defining-ai-adoption-criteria-through-reporting</id>
    <content type="html">&lt;p&gt;AI를 활용한 화면 개발 방식을 보고 자료로 정리하는 일이 있었습니다. 디자인 시스템과 개발 컴포넌트를 연결하고, 화면 코드 생성과 검토 방식을 검증한 내용을 짧은 문서에 담아야 했습니다.&lt;/p&gt;

&lt;p&gt;처음에는 준비한 구조와 도구의 동작을 설명하면 될 것 같았습니다. Figma에서 디자인을 확인하고, 기존 공통 컴포넌트를 활용해 코드를 생성하는 흐름을 보여주면 적용 방향도 전달할 수 있다고 생각했습니다.&lt;/p&gt;

&lt;p&gt;하지만 보고 내용을 정리하려면 무엇을 검증했고, 어디부터 실제 업무에 적용할 것이며, 어떤 효과는 앞으로 확인해야 하는지 나누어 설명할 필요가 있었습니다.&lt;/p&gt;

&lt;h2 id=&quot;검증한-범위를-성과의-기준으로-삼았습니다&quot;&gt;검증한 범위를 성과의 기준으로 삼았습니다&lt;/h2&gt;

&lt;p&gt;이번에 준비한 것은 Figma 디자인 시스템의 공통 컴포넌트를 React UI Library로 코드화하고, Storybook으로 문서화한 개발 기반이었습니다. 디자인 컴포넌트와 개발 컴포넌트의 연계, 화면 코드 생성 및 검토 방식에 대한 검증도 진행했습니다.&lt;/p&gt;

&lt;p&gt;이는 신규 화면 개발에 AI를 활용할 수 있는 기반을 마련했다는 의미였습니다. 다만 실제 업무 전반에서 생산성이 얼마나 달라졌는지까지 확인한 것은 아니었습니다.&lt;/p&gt;

&lt;p&gt;보고서에서는 준비한 자산과 검증한 방식을 먼저 설명하고, 신규 업무 적용과 효과 확인은 향후 계획으로 두었습니다. 기반을 확보한 일과 그 기반으로 얻을 효과를 구분해야 다음에 무엇을 확인할지도 분명해졌습니다.&lt;/p&gt;

&lt;h2 id=&quot;코드-생성-이후의-책임도-설명해야-했습니다&quot;&gt;코드 생성 이후의 책임도 설명해야 했습니다&lt;/h2&gt;

&lt;p&gt;AI가 화면 코드를 생성한다는 설명만으로는 서비스에 적용하는 과정을 충분히 보여주기 어려웠습니다. 결과를 누가 확인하고, 필요한 수정을 거쳐 어떻게 실제 개발에 사용할지가 함께 있어야 했습니다.&lt;/p&gt;

&lt;p&gt;이번 적용 방식에서는 기존 공통 컴포넌트를 우선 활용하고, 생성된 결과를 개발자가 검토·보완한 뒤 서비스 개발에 사용하는 흐름을 담았습니다. 컴포넌트 생성과 검토, 페이지 구현, 품질 검증을 연결하는 역할 구분도 포함했습니다.&lt;/p&gt;

&lt;p&gt;역할을 나누었다고 검토 책임이 사라지는 것은 아닙니다. 코드가 생성되었다는 사실과 업무에 사용할 수 있다는 판단은 구분해야 합니다. 개발자의 검토·보완을 적용 과정에 명시한 이유도 여기에 있었습니다.&lt;/p&gt;

&lt;h2 id=&quot;업무-적용-계획은-범위와-확인할-효과로-나누었습니다&quot;&gt;업무 적용 계획은 범위와 확인할 효과로 나누었습니다&lt;/h2&gt;

&lt;p&gt;적용 범위는 신규 모바일웹 화면과 UI 개발부터 시작하는 것으로 정리했습니다. 이후에는 디자인 검증, 코드 리뷰, 테스트로 확대하는 방향을 두되, 지금 준비한 범위와 앞으로 확장할 범위를 함께 묶어 성과로 표현하지 않았습니다.&lt;/p&gt;

&lt;p&gt;실제 적용 과정에서 확인할 항목도 보고 자료에 남겼습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;화면 개발의 생산성이 어떻게 달라지는가&lt;/li&gt;
  &lt;li&gt;생성 결과가 디자인 의도와 얼마나 일치하는가&lt;/li&gt;
  &lt;li&gt;기존 공통 컴포넌트를 얼마나 재사용하는가&lt;/li&gt;
  &lt;li&gt;적용 경험을 바탕으로 어떤 운영 기준을 보완해야 하는가&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 항목들은 실제 업무에 적용하면서 확인하려는 대상이었습니다. 코드 생성이 가능하다는 검증에서 나아가, 검토와 보완을 포함한 개발 과정에서 효과를 살펴볼 필요가 있다고 생각했습니다.&lt;/p&gt;

&lt;p&gt;디자인과 개발의 협업 기준도 실제 적용 과정에서 구체화할 계획으로 남겼습니다. 공통 컴포넌트를 우선 활용한다는 방향은 정했지만, 세부 운영 기준은 함께 조정해야 할 부분이었습니다.&lt;/p&gt;

&lt;h2 id=&quot;보고를-준비하며-다음-업무의-기준이-보였습니다&quot;&gt;보고를 준비하며 다음 업무의 기준이 보였습니다&lt;/h2&gt;

&lt;p&gt;자료를 정리하는 과정에서 이미 준비한 기반, 실제로 적용할 범위, 이후 확인할 효과가 서로 다른 설명이라는 점을 다시 보게 되었습니다. 각각을 분명하게 적어야 현재 위치와 다음 작업을 함께 전달할 수 있었습니다.&lt;/p&gt;

&lt;p&gt;이번 보고에서 생산성 향상을 숫자로 말할 수는 없었습니다. 대신 어떤 자산을 활용하고, 누가 결과를 검토하며, 무엇을 확인하면서 적용 범위를 넓힐 것인지는 설명할 수 있었습니다.&lt;/p&gt;

&lt;p&gt;보고를 준비하면서 검증한 범위를 출발점으로 삼고, 실제 업무에서 확인할 것을 정할 수 있었습니다.&lt;/p&gt;

&lt;p&gt;AI 업무 적용은 코드 생성 기능의 도입보다, 생성된 결과를 팀이 검토하고 활용할 수 있는 기준을 세우는 일에 가까웠습니다.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>API에서 고객이 사용하는 서비스까지</title>
    <link href="https://blog.archilog.dev/posts/from-backend-api-to-web-channel/"/>
    <updated>2026-09-30T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/from-backend-api-to-web-channel</id>
    <content type="html">&lt;p&gt;2026년 9월 29일, 모바일웹 프로젝트가 1차 오픈했습니다. 큰 이슈 없이 오픈을 마치고 나니, 이번에 만든 화면보다 그 화면을 만들기까지 쌓아 온 시간이 먼저 떠올랐습니다.&lt;/p&gt;

&lt;p&gt;지난 5년 동안 개발의 범위는 조금씩 넓어졌습니다. MTS에 데이터를 제공하는 API에서 시작해 고객이 직접 사용하는 커뮤니티 화면을 만들었고, 이제는 원장과 연결되는 금융 서비스로 이어졌습니다.&lt;/p&gt;

&lt;p&gt;처음부터 이 모든 과정을 하나의 플랫폼으로 만들겠다는 계획이 있었던 것은 아닙니다. 서비스를 하나씩 만들며 마주한 문제를 해결하다 보니, 다음 개발에서도 사용할 수 있는 기반이 쌓였습니다. 이번 오픈은 그 기반이 고객 서비스로 이어진 하나의 이정표였습니다.&lt;/p&gt;

&lt;h2 id=&quot;출발점은-서비스에-필요한-데이터를-제공하는-일이었습니다&quot;&gt;출발점은 서비스에 필요한 데이터를 제공하는 일이었습니다&lt;/h2&gt;

&lt;p&gt;처음에는 Spring Boot로 MTS의 커뮤니티, 통합검색, 투자정보 서비스에 필요한 API를 구축했습니다. 고객이 직접 보는 화면은 아니었지만, 화면에서 필요한 데이터를 안정적으로 제공하는 역할이었습니다.&lt;/p&gt;

&lt;p&gt;업무마다 필요한 데이터와 처리 방식은 달랐습니다. 그 안에서도 여러 서비스가 함께 사용할 수 있는 부분을 찾고, 공통으로 처리할 수 있는 기반을 마련해 갔습니다.&lt;/p&gt;

&lt;p&gt;당시에는 눈앞의 서비스를 구현하는 일이 중심이었습니다. 하지만 서비스가 늘어날수록 다음 개발에서 무엇을 다시 사용할 수 있을지 함께 생각하게 됐습니다. 한 번 해결한 문제를 다음 서비스에서도 처음부터 풀지 않으려면, 코드와 경험을 남기는 방식이 필요했습니다.&lt;/p&gt;

&lt;h2 id=&quot;api를-넘어-고객이-사용하는-화면으로&quot;&gt;API를 넘어 고객이 사용하는 화면으로&lt;/h2&gt;

&lt;p&gt;이후에는 Next.js로 커뮤니티 화면을 개발하고 SSO를 연동했습니다. API로 제공하던 서비스가 MTS 웹뷰 안에서 고객이 직접 사용하는 경험으로 이어졌습니다.&lt;/p&gt;

&lt;p&gt;화면을 만들기 시작하면서 살펴봐야 할 범위도 넓어졌습니다. 데이터를 정상적으로 전달하는 것에 더해, 고객이 어떤 경로로 들어오고 로그인 상태가 어떻게 이어지는지, 앱과 웹 사이의 이동이 자연스러운지도 함께 봐야 했습니다.&lt;/p&gt;

&lt;p&gt;앱이 담당할 기능과 웹이 담당할 기능, 서버에서 처리할 업무의 경계를 정하는 일도 필요했습니다. 각 영역을 나누면서도 고객에게는 하나의 서비스로 이어지도록 만드는 것이 중요했습니다.&lt;/p&gt;

&lt;p&gt;이 경험을 통해 개발의 결과를 바라보는 기준도 달라졌습니다. API와 화면 각각의 구현을 넘어, 고객이 서비스를 이용하는 전체 흐름을 함께 생각하게 됐습니다.&lt;/p&gt;

&lt;h2 id=&quot;금융-서비스를-만들기-위해-개발-기반을-정리했습니다&quot;&gt;금융 서비스를 만들기 위해 개발 기반을 정리했습니다&lt;/h2&gt;

&lt;p&gt;커뮤니티를 만들며 쌓은 경험을 바탕으로 원장 연동과 금융 서비스 개발을 시작했습니다. 고객의 계좌와 업무를 다루는 화면에서는 데이터 조회뿐 아니라 인증 상태와 업무 조건, 처리 결과까지 함께 고려해야 했습니다.&lt;/p&gt;

&lt;p&gt;이번에는 개별 화면을 만드는 과정과 함께, 앞으로 비슷한 업무를 어떻게 개발할지도 고민했습니다. 백엔드와 프런트엔드를 분리하고, 앱·웹·서버의 역할을 정리했습니다. 공통으로 사용할 기능과 업무별로 구현할 부분을 구분해 다음 서비스에서도 활용할 수 있는 구조를 마련했습니다.&lt;/p&gt;

&lt;p&gt;디자인 시스템을 실제 개발에 연결하는 작업도 진행했습니다. 공통 UI를 React 컴포넌트로 구현하고, Storybook에서 구성과 상태를 함께 확인할 수 있도록 했습니다.&lt;/p&gt;

&lt;p&gt;같은 버튼과 입력창을 화면마다 새로 구현하면 작은 차이가 쌓일 수 있습니다. 공통 컴포넌트가 있으면 개발자는 업무 흐름에 집중하고, 디자인과 개발은 같은 기준을 보며 결과를 확인할 수 있습니다. 재사용할 코드를 마련하는 일과 함께, 서로 확인하고 이야기할 기준을 만드는 일이기도 했습니다.&lt;/p&gt;

&lt;p&gt;이렇게 쌓인 기반을 하나의 웹 플랫폼으로 정리했습니다. API와 화면, 공통 기능과 UI 자산이 다음 개발로 이어질 수 있도록 묶은 것입니다.&lt;/p&gt;

&lt;h2 id=&quot;첫-오픈이-갖는-의미&quot;&gt;첫 오픈이 갖는 의미&lt;/h2&gt;

&lt;p&gt;이번에 오픈한 서비스의 규모는 크지 않습니다. 아직 만들어야 할 기능도 많습니다. 그럼에도 뜻깊은 이유는, 앞으로 다양한 금융 서비스를 이 채널 위에서 개발할 수 있는 기반을 실제 고객 서비스에 적용했기 때문입니다.&lt;/p&gt;

&lt;p&gt;API에서 시작한 개발이 커뮤니티 화면을 거쳐 금융 서비스 채널로 이어졌습니다. 각 단계에서 쌓은 경험을 다음 단계에 활용하면서, 하나의 서비스를 만드는 일이 공통 개발 기반을 만드는 일로 확장됐습니다.&lt;/p&gt;

&lt;p&gt;오픈은 여러 팀이 함께 맞춰 온 결과이기도 합니다. 기획과 디자인, 앱과 서버, 인증과 보안, 테스트와 운영이 연결돼야 고객이 사용할 수 있는 서비스가 됩니다. 함께 고민하고 만들어 준 팀원들, 오픈까지 힘을 보태주신 모든 분께 감사드립니다.&lt;/p&gt;

&lt;h2 id=&quot;앞으로는-이-기반을-더-잘-사용하는-개발을-하려-합니다&quot;&gt;앞으로는 이 기반을 더 잘 사용하는 개발을 하려 합니다&lt;/h2&gt;

&lt;p&gt;앞으로는 조회 중심의 서비스를 넘어 처리성 금융 업무로 범위를 넓혀 갈 예정입니다. 추가 인증과 전자서명, 업무 처리와 결과 확인이 이어지는 흐름에서 고객이 현재 상태를 이해하고 필요한 절차를 수행할 수 있도록 만드는 것이 다음 과제입니다.&lt;/p&gt;

&lt;p&gt;개발 자산도 계속 다듬으려 합니다. 공통 API와 UI 컴포넌트를 실제 업무에서 사용하며 부족한 부분을 보완하고, 사용 기준과 문서를 함께 관리하겠습니다. 새로 합류한 개발자도 기존 경험을 활용할 수 있어야 플랫폼이 팀의 자산으로 남을 수 있다고 생각합니다.&lt;/p&gt;

&lt;p&gt;AI를 활용한 화면 개발도 이 기반 위에서 이어 가려 합니다. Figma 디자인 시스템과 Storybook UI Library를 연결하고, Claude Code가 화면 구현을 보조하며 개발자가 결과를 검토하는 방식을 실제 업무에 적용해 갈 계획입니다.&lt;/p&gt;

&lt;p&gt;AI를 활용하면서도 중요하게 보는 것은 같은 기준입니다. 어떤 컴포넌트를 사용해야 하는지, 어떤 업무 규칙을 지켜야 하는지, 무엇을 확인해야 하는지가 분명해야 결과를 검토하고 개선할 수 있습니다. 그동안 정리한 개발 기준과 공통 자산이 이 과정에서도 역할을 할 것으로 기대합니다.&lt;/p&gt;

&lt;p&gt;오픈 이후의 운영도 함께 다듬어야 합니다. 변경 이력을 버전으로 관리하고, 배포와 모니터링, 보안 점검을 지속할 수 있도록 운영 기반을 정비해 가겠습니다. 새 서비스를 추가하는 일과 이미 제공하는 서비스를 안정적으로 유지하는 일이 함께 이어져야 합니다.&lt;/p&gt;

&lt;p&gt;지난 5년을 돌아보면, 개발의 범위가 넓어질 때마다 다음에 살펴봐야 할 문제도 달라졌습니다. 데이터를 제공하던 시기에는 화면과 고객의 흐름을, 화면을 만들던 시기에는 앱과 업무의 연결을, 금융 서비스를 만들면서는 공통 기준과 운영의 지속성을 더 생각하게 됐습니다.&lt;/p&gt;

&lt;p&gt;앞으로도 새로운 서비스를 만들며 그 기준을 계속 다듬어 가려 합니다. 다음 개발이 지금까지의 경험 위에서 시작할 수 있도록, 그리고 그 경험이 고객에게 더 편리한 서비스로 이어지도록 하겠습니다.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>React와 Spring Boot를 나눈 뒤, 배포 기준도 달라졌습니다</title>
    <link href="https://blog.archilog.dev/posts/splitting-ui-and-api-with-react-spring/"/>
    <updated>2026-09-28T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/splitting-ui-and-api-with-react-spring</id>
    <content type="html">&lt;p&gt;React와 Spring Boot로 구조를 나누면 프론트엔드와 백엔드의 역할이 명확해질 것이라고 생각했습니다.&lt;/p&gt;

&lt;p&gt;화면은 React가 담당하고, 데이터와 업무 처리는 Spring Boot API가 담당하면 된다는 설명은 단순합니다. 하지만 실제 업무 화면을 나누기 시작하니 어디까지가 화면의 책임이고 어디부터가 서버의 책임인지 애매한 지점이 계속 나타났습니다.&lt;/p&gt;

&lt;p&gt;역할을 정리한 뒤에는 또 다른 질문이 남았습니다. 코드는 나누었는데, 변경하고 빌드하고 배포하는 단위도 실제로 나뉘어 있는가 하는 문제였습니다.&lt;/p&gt;

&lt;h2 id=&quot;데이터를-보여주는-것과-업무를-판단하는-것은-달랐습니다&quot;&gt;데이터를 보여주는 것과 업무를 판단하는 것은 달랐습니다&lt;/h2&gt;

&lt;p&gt;화면은 사용자의 입력과 상태를 빠르게 표현해야 합니다. 입력값 형식이나 필수 항목을 확인하고, 로딩과 오류 상태를 보여주며, 이전 화면에서 이어진 흐름을 유지합니다.&lt;/p&gt;

&lt;p&gt;하지만 화면에서 확인했다고 업무 조건까지 신뢰할 수는 없었습니다. 계좌 상태, 상품 조건, 거래 가능 여부, 권한처럼 실제 처리 결과를 바꾸는 판단은 서버에서 다시 확인해야 했습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;화면은 빠른 피드백과 입력 편의를 담당합니다.&lt;/li&gt;
  &lt;li&gt;API는 업무 가능 여부와 요청 데이터의 정합성을 검증합니다.&lt;/li&gt;
  &lt;li&gt;원장이나 코어 서비스의 응답은 채널 API가 화면에서 사용할 수 있는 형태로 정리합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;같은 조건을 프론트엔드와 백엔드에 무작정 중복하는 것이 아니라, 각 검증이 필요한 이유를 구분하려고 했습니다. API도 원장 응답을 그대로 전달하지 않고, 화면에 필요한 데이터와 공통 오류 형식으로 다시 정리해 채널 서비스의 경계 역할을 맡도록 했습니다.&lt;/p&gt;

&lt;h2 id=&quot;책임은-나눴지만-변경-단위는-여전히-묶여-있었습니다&quot;&gt;책임은 나눴지만 변경 단위는 여전히 묶여 있었습니다&lt;/h2&gt;

&lt;p&gt;화면과 API의 책임을 정리하고 나니, 이번에는 변경을 어떤 단위로 관리할지 고민하게 되었습니다.&lt;/p&gt;

&lt;p&gt;화면만 수정했는데 여러 애플리케이션을 함께 빌드해야 하거나, 한 서비스의 변경을 다른 서비스와 같은 릴리즈에 맞춰야 한다면 코드의 분리가 운영의 독립성으로 이어졌다고 보기는 어려웠습니다.&lt;/p&gt;

&lt;p&gt;반대로 저장소를 나눈다고 이 문제가 저절로 해결되는 것도 아니었습니다. 저장소가 달라도 API 변경 때마다 함께 배포해야 한다면 두 영역의 변경은 여전히 연결되어 있습니다. 그래서 저장소를 몇 개로 나눌지보다, 무엇을 독립적으로 바꿀 수 있어야 하는지부터 다시 확인할 필요가 있었습니다.&lt;/p&gt;

&lt;h2 id=&quot;독립적으로-바꿀-수-있어야-하는-것을-다시-확인했습니다&quot;&gt;독립적으로 바꿀 수 있어야 하는 것을 다시 확인했습니다&lt;/h2&gt;

&lt;p&gt;프론트엔드, 게이트웨이, 업무 API는 모두 같은 서비스 흐름에 참여하지만 변경하는 이유와 주기가 반드시 같지는 않습니다.&lt;/p&gt;

&lt;p&gt;화면의 문구나 배치를 바꾸는 일, 요청을 연결하는 방식을 바꾸는 일, 업무 규칙을 바꾸는 일은 영향 범위가 다릅니다. 각 변경에 어떤 검증이 필요하고 어떤 애플리케이션을 다시 배포해야 하는지 설명할 수 있어야 했습니다.&lt;/p&gt;

&lt;p&gt;이때 독립 배포를 모든 변경에서 단독으로 배포할 수 있다는 뜻으로 보지는 않았습니다. API 계약이 달라지는 변경은 호환되는 조합과 적용 순서를 함께 확인해야 합니다. 독립성의 기준은 경계를 그어 놓았는지가 아니라, 변경의 영향을 확인하고 필요한 범위만 안전하게 내보낼 수 있는지에 가까웠습니다.&lt;/p&gt;

&lt;p&gt;여러 애플리케이션이 포함된 멀티모듈 구조를 다루면서는 모듈, 저장소, 배포 단위를 같은 의미로 취급하지 않는 것도 중요했습니다. 저장소는 소스와 협업의 경계이고, 모듈은 코드와 의존성의 경계이며, 배포물은 운영에 반영하는 단위였습니다.&lt;/p&gt;

&lt;h2 id=&quot;릴리즈-버전은-운영과의-약속이었습니다&quot;&gt;릴리즈 버전은 운영과의 약속이었습니다&lt;/h2&gt;

&lt;p&gt;개발 브랜치는 계속 바뀝니다. 같은 브랜치 이름이라도 어느 시점의 코드를 빌드했는지에 따라 결과가 달라질 수 있습니다. 그래서 브랜치 이름만으로 운영에 반영할 대상을 설명하기에는 부족했습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;어떤 소스 변경을 기준으로 빌드했는가&lt;/li&gt;
  &lt;li&gt;어떤 애플리케이션의 산출물이며 어떤 릴리즈에 포함되는가&lt;/li&gt;
  &lt;li&gt;검수한 산출물과 실제 반영할 산출물이 같은가&lt;/li&gt;
  &lt;li&gt;함께 사용하는 화면과 API의 버전 조합은 호환되는가&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;재현 가능한 배포를 위해서는 버전 이름을 붙이는 것에서 한 걸음 더 나아가야 했습니다. 소스 기준점뿐 아니라 의존성과 빌드 환경을 추적하고, 검증한 산출물을 보관해 이행 대상과 연결할 필요가 있었습니다.&lt;/p&gt;

&lt;h2 id=&quot;좋은-분리는-변경의-영향을-예측할-수-있게-합니다&quot;&gt;좋은 분리는 변경의 영향을 예측할 수 있게 합니다&lt;/h2&gt;

&lt;p&gt;처음에는 화면과 API의 책임을 나누는 데 집중했습니다. 이후에는 그 경계가 소스 관리와 빌드, 운영 반영까지 이어지는지 살펴보게 되었습니다.&lt;/p&gt;

&lt;p&gt;좋은 분리는 경계를 많이 만드는 일이 아니라는 생각이 들었습니다. 어떤 코드를 바꾸면 무엇을 확인해야 하고, 어느 산출물을 배포해야 하며, 문제가 생기면 어디로 돌아가야 하는지 설명할 수 있게 만드는 일에 가까웠습니다.&lt;/p&gt;

&lt;p&gt;고객은 프론트엔드와 백엔드의 경계를 구분하지 않습니다. React와 Spring Boot를 나눈 뒤 달라진 것은 기술 구조만이 아니었습니다. 변경을 준비하고 운영에 전달하는 기준도 함께 정리해야 하는 문제로 이어졌습니다.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>디자인 시스템은 AI Agent에게도 협업 기준이 될 수 있을까</title>
    <link href="https://blog.archilog.dev/posts/design-system-as-collaboration-standard/"/>
    <updated>2026-09-21T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/design-system-as-collaboration-standard</id>
    <content type="html">&lt;p&gt;Figma 화면을 AI로 구현해 보면서, 디자인 시스템을 다시 들여다보게 되었습니다.&lt;/p&gt;

&lt;p&gt;처음에는 결과 화면이 원본과 얼마나 비슷한지에 눈이 갔습니다. 그런데 모양이 비슷하다고 같은 컴포넌트를 사용한 것은 아니었습니다. 화면을 비교하는 것만으로는 놓치는 차이가 있었습니다.&lt;/p&gt;

&lt;p&gt;Figma에서 공통 컴포넌트와의 연결이 유지되어 있는지, Detach된 요소인지, 어떤 Variant를 선택했는지, 색상과 간격이 토큰에 연결되어 있는지가 구현을 판단하는 근거에 영향을 주었습니다.&lt;/p&gt;

&lt;p&gt;그때부터 질문이 조금 달라졌습니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;우리가 만든 디자인 시스템은 사람뿐 아니라 AI도 같은 기준으로 이해할 수 있는 상태일까?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;컴포넌트를-만들고-문서에-등록하면-된다고-생각했습니다&quot;&gt;컴포넌트를 만들고 문서에 등록하면 된다고 생각했습니다&lt;/h2&gt;

&lt;p&gt;디자인 시스템 구축 프로젝트에 참여하면서 처음에는 Figma에 정의된 컴포넌트를 React로 만들고 Storybook에 등록하면 된다고 생각했습니다.&lt;/p&gt;

&lt;p&gt;하지만 실제로는 디자인 의도를 개발자가 사용할 수 있는 인터페이스로 바꾸는 과정이 필요했습니다. 같은 버튼이라도 아이콘 유무, 로딩 상태, 비활성 상태, 전체 너비 사용 여부에 따라 구현 기준이 달라졌습니다.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;variant&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;size&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disabled&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loading&lt;/code&gt; 같은 Props는 단순한 속성이 아니라 컴포넌트를 만든 개발자와 사용하는 개발자 사이의 약속이었습니다. 디자인팀과 개발팀은 필수 상태, 접근성, 예외 처리까지 함께 정해야 했습니다.&lt;/p&gt;

&lt;p&gt;이때까지 디자인 시스템은 주로 사람 사이의 협업 기준이라고 생각했습니다. 문서에 빠진 내용이 있으면 서로 질문하고, 기존 화면을 찾아보며 의도를 맞출 수 있었습니다.&lt;/p&gt;

&lt;p&gt;AI가 화면 구현에 참여하면서는 그 빈칸을 어떻게 전달할지가 새로운 문제가 되었습니다.&lt;/p&gt;

&lt;h2 id=&quot;같은-화면처럼-보여도-같은-컴포넌트는-아니었습니다&quot;&gt;같은 화면처럼 보여도 같은 컴포넌트는 아니었습니다&lt;/h2&gt;

&lt;p&gt;Figma에서는 공통 컴포넌트의 인스턴스와 연결을 끊은 요소가 당장 같은 모양으로 보일 수 있습니다. 토큰을 참조하는 색상과 값을 직접 지정한 색상도 현재 값이 같다면 화면만 보고 구분하기 어렵습니다.&lt;/p&gt;

&lt;p&gt;하지만 이후 공통 기준이 바뀔 때 두 요소가 함께 바뀐다고 기대할 수는 없습니다. Variant와 상태 정의가 불명확하면 어떤 React 컴포넌트와 Props를 선택해야 하는지도 모호해집니다.&lt;/p&gt;

&lt;p&gt;이번 경험에서 AI는 시각적 결과뿐 아니라 전달된 Figma 내부 구조를 구현 근거로 활용했습니다. 사람이 보기에는 같은 버튼이어도 컴포넌트 연결이나 상태 정보가 다르면 같은 구현으로 이어진다고 기대하기 어려웠습니다.&lt;/p&gt;

&lt;p&gt;모든 AI 도구가 Figma를 같은 방식으로 읽는다는 뜻은 아닙니다. 다만 우리가 전달하는 구조와 맥락이 구현 판단에 영향을 준다는 점은 분명히 확인할 필요가 있었습니다.&lt;/p&gt;

&lt;p&gt;Detach 자체를 무조건 잘못된 작업으로 볼 수도 없었습니다. 의도적인 예외라면 왜 공통 컴포넌트를 벗어났는지, 그 예외를 코드에서는 어떻게 표현할지 남겨야 했습니다. 문제가 된 것은 차이 자체보다 그 차이를 설명할 기준이 없는 상태였습니다.&lt;/p&gt;

&lt;h2 id=&quot;구현-전에-확인할-기준을-더-명확하게-해야-했습니다&quot;&gt;구현 전에 확인할 기준을 더 명확하게 해야 했습니다&lt;/h2&gt;

&lt;p&gt;AI에게 화면을 맡기기 위해 전혀 새로운 기준이 필요한 것은 아니었습니다. 사람이 협업할 때 필요했던 약속을 더 명시적으로 정리하는 일이 먼저였습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Figma 컴포넌트가 어떤 React 컴포넌트와 연결되는지&lt;/li&gt;
  &lt;li&gt;Variant와 상태를 어떤 Props로 표현하며 어떤 조합을 허용하는지&lt;/li&gt;
  &lt;li&gt;색상·간격·폰트가 어떤 디자인 토큰을 참조하는지&lt;/li&gt;
  &lt;li&gt;공통 컴포넌트를 벗어나도 되는 경우와 그 이유는 무엇인지&lt;/li&gt;
  &lt;li&gt;구현 후 시각적 결과, 상태 변화, 키보드 동작과 포커스를 어떻게 확인할지&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;예를 들어 버튼의 색상이 같다는 사실과 같은 의미의 토큰을 사용했다는 사실은 다릅니다. 지금 보이는 값을 맞추는 데서 끝내면 이후 정책 변경을 함께 반영하기 어렵습니다.&lt;/p&gt;

&lt;p&gt;컴포넌트 이름만 같아도 충분하지 않았습니다. 어떤 상태를 지원하고 어디까지 책임지는지 연결되어야 화면을 만드는 쪽에서도 재사용 여부를 판단할 수 있었습니다.&lt;/p&gt;

&lt;p&gt;이 기준은 AI에게 더 많은 설명을 붙이기 위한 문서라기보다, 다음 구현에서도 같은 판단을 반복할 수 있도록 남기는 개발 자산에 가까웠습니다.&lt;/p&gt;

&lt;h2 id=&quot;만드는-역할과-확인하는-역할에도-같은-기준이-필요했습니다&quot;&gt;만드는 역할과 확인하는 역할에도 같은 기준이 필요했습니다&lt;/h2&gt;

&lt;p&gt;Builder·Review·Audit 역할을 나누면서는 검증 기준의 필요성이 더 분명해졌습니다.&lt;/p&gt;

&lt;p&gt;화면을 만드는 역할과 결과를 검토하는 역할을 분리해도, 무엇을 확인할지가 불분명하면 각자 다른 기준으로 완료를 판단할 수 있었습니다. 모양이 비슷한지 확인하는 것만으로는 컴포넌트 재사용이나 토큰 연결까지 확인했다고 말하기 어려웠습니다.&lt;/p&gt;

&lt;p&gt;그래서 역할 이름보다 각 역할이 확인할 근거를 정하는 일이 중요하다고 느꼈습니다. Builder는 사용할 컴포넌트와 허용된 Props를 참조하고, Review는 의도한 화면과 상태가 구현되었는지 확인하며, Audit은 공통 기준에서 벗어난 부분과 그 이유를 확인하는 식으로 책임을 구체화할 필요가 있었습니다.&lt;/p&gt;

&lt;p&gt;이 구분만으로 품질이 보장되는 것은 아닙니다. 같은 자료를 참조하더라도 빠진 예외나 잘못된 기준은 사람이 다시 판단해야 합니다. 역할 분리는 그 판단이 필요한 지점을 찾기 위한 방법이어야 했습니다.&lt;/p&gt;

&lt;h2 id=&quot;storybook은-함께-참조하는-구현-기준으로-확장되었습니다&quot;&gt;Storybook은 함께 참조하는 구현 기준으로 확장되었습니다&lt;/h2&gt;

&lt;p&gt;Storybook도 조금 다르게 보이기 시작했습니다.&lt;/p&gt;

&lt;p&gt;처음에는 구현한 컴포넌트를 보여주고 디자인팀과 검수하는 문서라고 생각했습니다. 이제는 어떤 컴포넌트가 존재하고, 어떤 상태와 조합을 지원하며, 어떻게 사용해야 하는지 사람과 AI가 함께 참조할 수 있는 자산으로 볼 필요가 있었습니다.&lt;/p&gt;

&lt;p&gt;기본 모습 하나만 등록되어 있다면 실제 화면에서 필요한 판단은 여전히 많이 남습니다. 로딩, 비활성, 오류와 같은 상태, 허용하지 않는 조합, 접근성 기준까지 연결되어 있어야 재사용할 때의 추측을 줄일 수 있습니다.&lt;/p&gt;

&lt;p&gt;물론 Storybook에 등록했다는 사실만으로 AI가 그 내용을 자동으로 활용하는 것은 아닙니다. 작업에 필요한 컴포넌트 문서와 사용 예제를 참조할 수 있게 전달하고, 그 자료가 실제 코드와 일치하도록 유지해야 합니다.&lt;/p&gt;

&lt;p&gt;Figma는 디자인 의도와 상태를, React 컴포넌트는 구현 가능한 인터페이스를, Storybook은 사용 예제와 검수할 상태를 보여줍니다. 이 셋의 연결이 유지될 때 다음 화면도 같은 기준에서 시작할 수 있습니다.&lt;/p&gt;

&lt;h2 id=&quot;ai-도입은-이미-가진-자산을-다시-정리하는-일이기도-했습니다&quot;&gt;AI 도입은 이미 가진 자산을 다시 정리하는 일이기도 했습니다&lt;/h2&gt;

&lt;p&gt;AI로 화면을 만들어 보는 경험은 새로운 도구를 사용하는 일에서 시작했습니다. 하지만 그 과정에서 다시 확인하게 된 것은 기존 디자인 시스템의 연결과 운영 방식이었습니다.&lt;/p&gt;

&lt;p&gt;디자인 시스템은 사람 사이의 약속에서 시작했지만, AI가 화면을 구현하기 시작하면서 기계도 참조하고 검증할 수 있도록 표현된 기준이 필요해졌습니다.&lt;/p&gt;

&lt;p&gt;중요한 것은 AI가 얼마나 빠르게 화면을 만드는지만이 아니었습니다. 어떤 자산을 재사용하게 할지, 어디까지 허용할지, 결과가 기준을 지켰는지 무엇으로 확인할지가 먼저 준비되어 있어야 했습니다.&lt;/p&gt;

&lt;p&gt;이번 경험을 통해 AI 도입의 출발점은 지금까지 쌓아 온 컴포넌트와 문서, 협업 기준을 다시 정리하는 일이기도 하다는 생각을 하게 되었습니다.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>원장 메시지는 왜 그대로 고객에게 보여주기 어려울까</title>
    <link href="https://blog.archilog.dev/posts/why-core-messages-need-customer-language/"/>
    <updated>2026-09-14T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/why-core-messages-need-customer-language</id>
    <content type="html">&lt;p&gt;모바일 웹서비스에 다국어 지원을 미리 준비해두면 좋겠다는 생각으로 화면에 노출되는 메시지를 하나씩 살펴보기 시작했습니다.&lt;/p&gt;

&lt;p&gt;처음에는 기존 메시지를 리소스 파일로 옮기고 언어별 문구를 연결하면 될 것이라고 생각했습니다. 그런데 원장과 여러 내부 시스템에서 전달되는 메시지를 들여다보니, 번역 전에 먼저 고민해야 할 것이 있었습니다.&lt;/p&gt;

&lt;p&gt;이 메시지를 고객에게 그대로 보여줘도 되는가 하는 문제였습니다.&lt;/p&gt;

&lt;h2 id=&quot;시스템에는-정확하지만-고객에게는-어려웠습니다&quot;&gt;시스템에는 정확하지만 고객에게는 어려웠습니다&lt;/h2&gt;

&lt;p&gt;원장 메시지는 업무 처리 상태를 구분하고 운영자가 원인을 확인하는 데 필요한 정보입니다.&lt;/p&gt;

&lt;p&gt;업무 코드와 내부 용어, 처리 조건이 포함되어 있어 시스템 관점에서는 정확할 수 있습니다. 하지만 고객은 내부 구조를 알지 못합니다. 같은 메시지를 보더라도 무엇을 확인해야 하는지, 다시 시도하면 되는지, 상담이 필요한지 판단하기 어렵습니다.&lt;/p&gt;

&lt;p&gt;하나의 원인에 비슷한 기술 메시지가 여러 개 존재하기도 했습니다. 반대로 고객에게는 서로 다르게 안내해야 하는 상황이 같은 메시지로 묶여 있는 경우도 있었습니다.&lt;/p&gt;

&lt;p&gt;기술 메시지를 그대로 번역하면 언어만 바뀔 뿐 이해하기 어려운 문제는 그대로 남습니다.&lt;/p&gt;

&lt;h2 id=&quot;메시지를-문장이-아니라-행동-기준으로-봤습니다&quot;&gt;메시지를 문장이 아니라 행동 기준으로 봤습니다&lt;/h2&gt;

&lt;p&gt;고객 메시지를 정리하면서 문구의 자연스러움보다 고객이 다음에 무엇을 할 수 있는지를 먼저 보기로 했습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;입력 정보를 다시 확인하면 되는지&lt;/li&gt;
  &lt;li&gt;잠시 후 재시도할 수 있는지&lt;/li&gt;
  &lt;li&gt;로그인이나 인증을 다시 해야 하는지&lt;/li&gt;
  &lt;li&gt;현재 조건에서는 업무를 진행할 수 없는지&lt;/li&gt;
  &lt;li&gt;고객센터나 담당자 확인이 필요한지&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;원장의 상세 메시지는 로그와 운영 추적을 위해 보존하되, 화면에는 고객이 이해하고 행동할 수 있는 메시지를 제공하는 방향을 검토했습니다.&lt;/p&gt;

&lt;p&gt;이를 위해 원장 메시지와 고객 메시지를 일대일로 단순 치환하기보다 업무 상황과 오류 코드, 화면 맥락을 함께 매핑해야 했습니다.&lt;/p&gt;

&lt;h2 id=&quot;공통화와-구체성-사이의-균형이-필요했습니다&quot;&gt;공통화와 구체성 사이의 균형이 필요했습니다&lt;/h2&gt;

&lt;p&gt;메시지를 너무 세분화하면 관리해야 할 문구가 계속 늘어납니다. 반대로 몇 개의 공통 메시지로만 줄이면 고객이 왜 업무를 진행할 수 없는지 알기 어렵습니다.&lt;/p&gt;

&lt;p&gt;이번에는 고객의 다음 행동이 같다면 공통 메시지로 묶고, 다른 행동이 필요하다면 메시지를 분리하는 기준을 사용했습니다.&lt;/p&gt;

&lt;p&gt;예를 들어 여러 기술 원인이 있더라도 고객이 입력값을 확인해야 하는 상황이라면 하나의 안내 유형으로 정리할 수 있습니다. 하지만 재로그인이 필요한 상황과 거래 조건을 충족하지 못한 상황은 서로 다른 안내가 필요합니다.&lt;/p&gt;

&lt;p&gt;다국어 리소스에는 고객 메시지 키를 저장하고, 원장의 상세 코드와 원문은 서버 로그와 추적 정보에 남기는 구조를 함께 생각했습니다.&lt;/p&gt;

&lt;h2 id=&quot;메시지는-api-응답의-마지막-필드가-아니었습니다&quot;&gt;메시지는 API 응답의 마지막 필드가 아니었습니다&lt;/h2&gt;

&lt;p&gt;메시지를 정리하기 전에는 오류 응답에 들어가는 문구 정도로 생각하기 쉬웠습니다.&lt;/p&gt;

&lt;p&gt;하지만 고객이 업무를 중단하게 되는 순간에는 메시지가 서비스 경험의 대부분이 됩니다. 무엇이 잘못되었는지보다 이제 무엇을 하면 되는지를 알려주는 것이 더 중요할 때도 있습니다.&lt;/p&gt;

&lt;p&gt;다국어 지원을 준비하며 시작한 작은 점검은 원장 메시지와 고객 메시지의 역할을 다시 나누는 일로 이어졌습니다.&lt;/p&gt;

&lt;p&gt;좋은 고객 메시지는 기술적인 원인을 감추는 문장이 아니라, 내부의 복잡한 상황을 고객이 이해할 수 있는 다음 행동으로 바꾸는 번역이라고 생각합니다.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Storybook은 예쁜 컴포넌트 문서가 아니라 협업 기준이다</title>
    <link href="https://blog.archilog.dev/posts/storybook-as-collaboration-standard/"/>
    <updated>2026-09-07T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/storybook-as-collaboration-standard</id>
    <content type="html">&lt;p&gt;Storybook을 처음 구성할 때는 개발된 컴포넌트를 보기 좋게 모아두는 문서라고 생각했습니다.&lt;/p&gt;

&lt;p&gt;버튼과 입력창, 탭, 모달을 화면에 나열하고 Props를 확인할 수 있으면 역할을 다한 것처럼 보였습니다.&lt;/p&gt;

&lt;p&gt;하지만 디자인 시스템을 실제 프로젝트에서 사용하기 시작하니 Storybook에 남겨야 할 것은 완성된 모양보다 컴포넌트를 사용하는 기준이었습니다.&lt;/p&gt;

&lt;h2 id=&quot;한-가지-예제로는-컴포넌트를-설명하기-어려웠습니다&quot;&gt;한 가지 예제로는 컴포넌트를 설명하기 어려웠습니다&lt;/h2&gt;

&lt;p&gt;버튼 하나에도 기본, 비활성화, 로딩, 아이콘 포함, 크기와 종류별 상태가 있습니다. 입력 컴포넌트에는 값이 없는 상태와 입력 중인 상태, 오류, 도움말, 읽기 전용 상태가 존재합니다.&lt;/p&gt;

&lt;p&gt;기본 화면만 등록하면 컴포넌트가 정상적으로 보인다는 사실은 확인할 수 있습니다. 하지만 화면 개발자가 어느 Props를 사용할 수 있는지, 조합하면 안 되는 상태가 무엇인지 알기는 어렵습니다.&lt;/p&gt;

&lt;p&gt;그래서 Story에는 실제 서비스에서 마주할 수 있는 상태를 중심으로 담기로 했습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;기본 사용 예시와 자주 쓰는 조합&lt;/li&gt;
  &lt;li&gt;빈 값, 긴 텍스트, 오류 같은 경계 조건&lt;/li&gt;
  &lt;li&gt;허용되는 Props와 기본값&lt;/li&gt;
  &lt;li&gt;키보드와 포커스 동작&lt;/li&gt;
  &lt;li&gt;사용하면 안 되는 사례&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;예쁜 예시를 많이 만드는 것보다 개발자가 잘못 사용하기 쉬운 지점을 먼저 보여주는 편이 더 유용했습니다.&lt;/p&gt;

&lt;h2 id=&quot;figma와-코드가-만나는-검수-공간이-필요했습니다&quot;&gt;Figma와 코드가 만나는 검수 공간이 필요했습니다&lt;/h2&gt;

&lt;p&gt;Figma는 디자인 기준을 보여주고, React 코드는 실제 동작을 만듭니다. 두 결과가 같은지는 한쪽 도구만으로 확인하기 어렵습니다.&lt;/p&gt;

&lt;p&gt;Storybook에서는 개발된 컴포넌트의 상태를 독립적으로 확인할 수 있습니다. 디자인팀은 색상과 간격, 상태 표현이 의도와 맞는지 보고, 개발팀은 Props와 이벤트, 접근성과 예외 상태를 함께 검증할 수 있습니다.&lt;/p&gt;

&lt;p&gt;검수 시점을 서비스 화면이 완성된 뒤로 미루면 컴포넌트 문제와 화면 조합 문제를 분리하기 어렵습니다. 컴포넌트 단계에서 먼저 확인하면 수정 범위가 더 작고 기준도 명확했습니다.&lt;/p&gt;

&lt;p&gt;Storybook은 개발 결과를 전달하는 마지막 단계라기보다 디자인과 개발이 중간 결과를 함께 확인하는 공간에 가까웠습니다.&lt;/p&gt;

&lt;h2 id=&quot;변경-이력을-컴포넌트-가까이에-남겼습니다&quot;&gt;변경 이력을 컴포넌트 가까이에 남겼습니다&lt;/h2&gt;

&lt;p&gt;디자인 시스템은 한 번 만들고 끝나지 않습니다. 버튼 높이가 바뀌거나 입력 오류 상태가 추가되고, 모달 동작 기준이 달라질 수 있습니다.&lt;/p&gt;

&lt;p&gt;변경 이유와 영향 범위가 남지 않으면 기존 화면이 어떤 기준으로 만들어졌는지 추적하기 어렵습니다. 그래서 컴포넌트의 문서와 변경 이력을 구현체 가까이에 두는 방식을 검토했습니다.&lt;/p&gt;

&lt;p&gt;Storybook Docs에는 사용 목적과 Props, 상태별 예시를 남기고, changelog에는 무엇이 바뀌었으며 어떤 화면의 확인이 필요한지 기록합니다.&lt;/p&gt;

&lt;p&gt;단순히 최신 결과만 보여주는 것이 아니라 그 결과가 어떤 기준을 거쳐 현재 모습이 되었는지도 확인할 수 있게 하는 것이 목적이었습니다.&lt;/p&gt;

&lt;h2 id=&quot;문서가-아니라-반복-가능한-협업-흐름이었습니다&quot;&gt;문서가 아니라 반복 가능한 협업 흐름이었습니다&lt;/h2&gt;

&lt;p&gt;Storybook을 운영하려면 등록 기준도 단순해야 합니다.&lt;/p&gt;

&lt;p&gt;컴포넌트를 구현하고 Story를 추가한 뒤 개발팀이 동작을 검토하고, 디자인팀이 시각적 기준을 확인합니다. 변경사항을 기록한 다음 서비스 화면에서 사용하도록 흐름을 맞췄습니다.&lt;/p&gt;

&lt;p&gt;Storybook이 일부 개발자만 보는 전시장에 머무르면 시간이 지나면서 실제 코드와 달라질 수 있습니다. 컴포넌트 변경과 Story 변경을 같은 작업으로 다뤄야 문서와 구현이 함께 유지됩니다.&lt;/p&gt;

&lt;p&gt;좋은 Storybook은 예쁜 컴포넌트를 많이 보여주는 곳이 아니었습니다. 디자인팀과 개발팀, 화면 개발자가 같은 컴포넌트를 같은 기준으로 이해하도록 만드는 협업의 접점이라고 생각합니다.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>금융권 개발망에서 오픈소스와 개발 생산성을 함께 고민하는 법</title>
    <link href="https://blog.archilog.dev/posts/open-source-productivity-in-financial-network/"/>
    <updated>2026-08-31T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/open-source-productivity-in-financial-network</id>
    <content type="html">&lt;p&gt;금융권 개발망에서 프로젝트를 준비하다 보면 코드보다 먼저 개발 환경을 살펴보게 됩니다.&lt;/p&gt;

&lt;p&gt;필요한 라이브러리를 바로 설치할 수 없고, 공식 문서나 예제를 확인할 수 있는 도메인도 제한됩니다. 오픈소스 하나를 추가하려면 반입 절차와 취약점, 라이선스까지 함께 확인해야 합니다.&lt;/p&gt;

&lt;p&gt;처음에는 이런 절차를 개발을 늦추는 제약으로만 바라본 적도 있습니다. 하지만 고객 정보와 거래 시스템을 다루는 환경에서 보안 기준은 가볍게 볼 수 없는 조건입니다.&lt;/p&gt;

&lt;p&gt;그렇다고 개발 생산성을 포기할 수도 없었습니다.&lt;/p&gt;

&lt;h2 id=&quot;오픈소스-하나가-의존성-하나로-끝나지-않았습니다&quot;&gt;오픈소스 하나가 의존성 하나로 끝나지 않았습니다&lt;/h2&gt;

&lt;p&gt;개발자가 직접 선택한 라이브러리는 하나여도 실제 프로젝트에는 여러 하위 의존성이 함께 들어옵니다.&lt;/p&gt;

&lt;p&gt;직접 사용하는 패키지의 라이선스가 문제없어 보여도 하위 라이브러리에 다른 라이선스나 알려진 취약점이 포함될 수 있습니다. 버전이 조금 달라지면 개발자 PC에서는 되지만 빌드 서버에서는 실패하는 문제도 생깁니다.&lt;/p&gt;

&lt;p&gt;그래서 오픈소스 반입은 파일을 외부에서 내부로 옮기는 작업이 아니라 변경관리의 일부에 가까웠습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;어떤 기능을 위해 사용하는지&lt;/li&gt;
  &lt;li&gt;직접·하위 의존성에 어떤 라이선스가 있는지&lt;/li&gt;
  &lt;li&gt;알려진 취약점과 대체 버전은 무엇인지&lt;/li&gt;
  &lt;li&gt;내부 저장소에 어떤 버전으로 등록할지&lt;/li&gt;
  &lt;li&gt;문제가 생겼을 때 누가 변경 이력을 확인할지&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 기준이 있어야 같은 검토를 매번 처음부터 반복하지 않을 수 있었습니다.&lt;/p&gt;

&lt;h2 id=&quot;nexus는-패키지-저장소-이상의-역할을-했습니다&quot;&gt;Nexus는 패키지 저장소 이상의 역할을 했습니다&lt;/h2&gt;

&lt;p&gt;폐쇄망에서는 Maven Central이나 npm Registry에 직접 접근할 수 없습니다. 필요한 패키지는 검토와 반입 절차를 거쳐 내부 Nexus Repository에 등록해야 합니다.&lt;/p&gt;

&lt;p&gt;Nexus를 기준으로 의존성을 관리하면 개발자 PC와 빌드 서버가 같은 패키지를 사용하게 됩니다. 승인된 버전을 팀 전체가 재사용할 수 있고, 외부망 연결 없이도 동일한 조건으로 빌드할 수 있습니다.&lt;/p&gt;

&lt;p&gt;하지만 저장소만 만든다고 기준이 완성되는 것은 아니었습니다.&lt;/p&gt;

&lt;p&gt;새 버전을 누가 요청하고 검토할지, 취약점이 발견되면 어떤 버전으로 교체할지, 사용하지 않는 패키지는 어떻게 정리할지까지 운영 절차가 필요했습니다. 기술 인프라와 관리 기준이 함께 있어야 내부 저장소가 실제 생산성으로 이어졌습니다.&lt;/p&gt;

&lt;h2 id=&quot;모든-산출물을-같은-방식으로-볼-필요가-있을까&quot;&gt;모든 산출물을 같은 방식으로 볼 필요가 있을까&lt;/h2&gt;

&lt;p&gt;프로젝트를 진행하면서 산출물의 성격과 위험도를 구분할 필요도 느꼈습니다.&lt;/p&gt;

&lt;p&gt;고객 정보, 인증, 거래, 원장 연동과 관련된 코드는 통제된 개발망에서 엄격하게 다뤄야 합니다. 반면 비즈니스 로직이 없는 UI 컴포넌트, 디자인 토큰, Storybook 문서처럼 상대적으로 위험도가 낮은 산출물은 승인된 범위에서 다른 개발 방식을 검토할 수 있습니다.&lt;/p&gt;

&lt;p&gt;이 구분이 보안 기준을 낮추자는 뜻은 아닙니다. 무엇을 보호해야 하는지 더 명확히 하고, 위험도가 낮은 영역에는 안전하게 생산성을 높일 수 있는 절차를 만드는 쪽에 가깝습니다.&lt;/p&gt;

&lt;h2 id=&quot;차단과-허용-사이에-기준이-필요했습니다&quot;&gt;차단과 허용 사이에 기준이 필요했습니다&lt;/h2&gt;

&lt;p&gt;금융권 개발환경을 일반 인터넷 환경과 똑같이 만들 수는 없습니다. 그러나 필요한 도구를 일괄적으로 차단하는 것만으로는 변화하는 개발 방식을 따라가기 어렵습니다.&lt;/p&gt;

&lt;p&gt;어떤 오픈소스를 어떤 절차로 사용할 수 있는지, 낮은 위험도의 산출물에는 어떤 개발 방식을 허용할 수 있는지, 검토 결과와 변경 이력을 어디에 남길지를 함께 정해야 합니다.&lt;/p&gt;

&lt;p&gt;보안과 생산성은 서로 반대편에 놓인 목표라기보다 함께 운영해야 하는 조건이라고 생각합니다. 개발자가 더 안전하게, 그리고 더 잘 일할 수 있도록 만드는 구체적인 기준이 그 사이를 연결합니다.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>작은 문구 수정은 왜 CMS에 대한 고민으로 이어졌을까</title>
    <link href="https://blog.archilog.dev/posts/small-copy-change-led-to-cms/"/>
    <updated>2026-08-24T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/small-copy-change-led-to-cms</id>
    <content type="html">&lt;p&gt;React 기반 웹서비스로 전환을 준비하면서 작은 고민이 하나 생겼습니다.&lt;/p&gt;

&lt;p&gt;화면의 안내 문구 하나를 바꾸기 위해 개발자가 코드를 수정하고, 테스트하고, 다시 배포하는 방식이 앞으로도 맞을까 하는 질문이었습니다.&lt;/p&gt;

&lt;p&gt;문구 수정 자체는 어렵지 않습니다. 하지만 변경이 발생할 때마다 개발과 배포가 필요하다면 운영 속도는 계속 개발 일정에 묶이게 됩니다.&lt;/p&gt;

&lt;h2 id=&quot;처음에는-react로-모두-만들면-된다고-생각했습니다&quot;&gt;처음에는 React로 모두 만들면 된다고 생각했습니다&lt;/h2&gt;

&lt;p&gt;JSP 화면을 React로 옮기면서 화면과 API의 책임을 분리하는 것이 우선이었습니다.&lt;/p&gt;

&lt;p&gt;React는 공통 컴포넌트와 상태를 관리하기 좋고, 서비스 화면을 일관된 기준으로 만들 수 있습니다. 그래서 메뉴, 배너, 공지, 안내 콘텐츠도 React 화면 안에서 관리하면 된다고 생각했습니다.&lt;/p&gt;

&lt;p&gt;하지만 운영 관점에서 다시 보니 성격이 다른 영역이 섞여 있었습니다.&lt;/p&gt;

&lt;p&gt;업무 규칙과 연결된 화면은 개발과 검증이 필요합니다. 반면 배너 이미지, 공지, 메뉴 노출 여부, 설명 문구처럼 자주 바뀌는 콘텐츠는 운영자가 시점에 맞춰 직접 관리하는 편이 더 자연스러울 수 있습니다.&lt;/p&gt;

&lt;h2 id=&quot;변경-빈도와-위험도를-함께-봤습니다&quot;&gt;변경 빈도와 위험도를 함께 봤습니다&lt;/h2&gt;

&lt;p&gt;모든 화면을 CMS로 관리하면 유연해 보이지만, 업무 로직까지 콘텐츠처럼 다루면 검증하기 어려워집니다. 반대로 모든 변경을 개발 코드에 두면 작은 운영 변경에도 배포가 반복됩니다.&lt;/p&gt;

&lt;p&gt;그래서 기능의 성격을 기준으로 나누기 시작했습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;거래와 신청처럼 업무 규칙이 있는 영역은 애플리케이션이 담당합니다.&lt;/li&gt;
  &lt;li&gt;인증, 권한, 고객 데이터와 연결된 내용은 서버 검증을 유지합니다.&lt;/li&gt;
  &lt;li&gt;배너, 공지, 메뉴, 단순 안내 콘텐츠는 CMS 관리 대상으로 검토합니다.&lt;/li&gt;
  &lt;li&gt;공통 레이아웃과 디자인 시스템은 React 컴포넌트로 유지합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;구분 기준은 단순히 자주 바뀌는지 여부만은 아니었습니다. 잘못 변경되었을 때 고객과 업무에 미치는 영향, 별도의 승인과 검수가 필요한지, 정형화된 데이터로 관리할 수 있는지도 함께 봐야 했습니다.&lt;/p&gt;

&lt;h2 id=&quot;cms는-개발을-대신하는-도구가-아니었습니다&quot;&gt;CMS는 개발을 대신하는 도구가 아니었습니다&lt;/h2&gt;

&lt;p&gt;CMS를 도입하면 개발자가 필요 없어지는 것이 아니라 개발이 책임져야 할 영역이 더 분명해집니다.&lt;/p&gt;

&lt;p&gt;개발팀은 운영자가 안전하게 변경할 수 있는 콘텐츠 모델과 권한, 미리보기, 이력 관리, 배포 기준을 만들어야 합니다. 운영팀은 정해진 범위 안에서 콘텐츠를 등록하고 검수합니다.&lt;/p&gt;

&lt;p&gt;React 화면은 CMS에서 받은 데이터를 디자인 시스템 기준에 맞게 표현하고, 값이 없거나 잘못되었을 때도 화면이 무너지지 않도록 처리해야 합니다.&lt;/p&gt;

&lt;p&gt;CMS와 React의 관계는 서로 대체하는 구조가 아니라 역할을 나누는 구조에 가까웠습니다.&lt;/p&gt;

&lt;h2 id=&quot;작은-문구가-플랫폼의-운영-방식을-보여줬습니다&quot;&gt;작은 문구가 플랫폼의 운영 방식을 보여줬습니다&lt;/h2&gt;

&lt;p&gt;문구 하나를 바꾸는 일은 작아 보입니다. 하지만 그 변경을 누가, 어떤 절차로, 어느 시점에 반영할지는 플랫폼의 운영 방식과 연결됩니다.&lt;/p&gt;

&lt;p&gt;개발 없이 바꿀 수 있는 범위를 넓히는 것만이 목표는 아니었습니다. 개발과 검증이 필요한 영역은 지키고, 운영이 직접 책임질 수 있는 영역에는 적절한 도구와 기준을 제공하는 것이 중요했습니다.&lt;/p&gt;

&lt;p&gt;CMS에 대한 고민은 기능 하나를 추가하는 데서 시작하지 않았습니다. 작은 변경을 반복해서 안전하게 다루려면 어떤 구조가 필요한지 생각하면서 자연스럽게 시작되었습니다.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Swagger와 REST Docs를 같이 가져가는 이유</title>
    <link href="https://blog.archilog.dev/posts/why-swagger-and-rest-docs-together/"/>
    <updated>2026-08-17T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/why-swagger-and-rest-docs-together</id>
    <content type="html">&lt;p&gt;내부 개발망에서 Swagger와 Spring REST Docs를 설정하면서 예상보다 오랜 시간을 보낸 적이 있습니다.&lt;/p&gt;

&lt;p&gt;처음에는 Swagger 화면을 열고 REST Docs 문서가 생성되게 하면 끝나는 작업이라고 생각했습니다. 하지만 라이브러리 반입 여부부터 Gradle Task, 테스트와 문서 생성 경로까지 하나씩 맞춰야 했습니다.&lt;/p&gt;

&lt;p&gt;설정을 들여다볼수록 두 도구를 같이 사용하는 이유도 더 분명해졌습니다.&lt;/p&gt;

&lt;h2 id=&quot;swagger는-개발-중의-대화에-가까웠습니다&quot;&gt;Swagger는 개발 중의 대화에 가까웠습니다&lt;/h2&gt;

&lt;p&gt;Swagger는 API 목록과 요청·응답 구조를 바로 확인하고 직접 호출해볼 수 있습니다.&lt;/p&gt;

&lt;p&gt;프론트엔드 개발자는 아직 화면에 연결하지 않은 API도 브라우저에서 확인할 수 있고, 백엔드 개발자는 인증 헤더와 요청 값이 예상대로 처리되는지 빠르게 점검할 수 있습니다.&lt;/p&gt;

&lt;p&gt;하지만 화면을 띄우는 것만으로는 충분하지 않았습니다. API를 업무 기준으로 분류하고, 공통 인증 헤더와 서버 주소를 맞추며, 요청과 응답의 설명을 같은 방식으로 작성해야 했습니다.&lt;/p&gt;

&lt;p&gt;기준이 없으면 Swagger가 있어도 API마다 표현 방식이 달라집니다. 개발자가 빠르게 확인할 수 있다는 장점은 문서화 규칙이 함께 있을 때 더 잘 작동했습니다.&lt;/p&gt;

&lt;h2 id=&quot;rest-docs는-검증된-결과를-남겼습니다&quot;&gt;REST Docs는 검증된 결과를 남겼습니다&lt;/h2&gt;

&lt;p&gt;Spring REST Docs는 테스트 결과를 기반으로 문서 조각을 생성합니다. 작성한 테스트가 성공해야 요청과 응답 예시, 필드 설명이 문서에 포함됩니다.&lt;/p&gt;

&lt;p&gt;Swagger가 개발 중 빠르게 확인하고 대화하기 위한 도구라면 REST Docs는 테스트로 확인한 API 계약을 남기는 도구에 가까웠습니다.&lt;/p&gt;

&lt;p&gt;물론 테스트 코드와 문서 작성 비용이 생깁니다. 모든 API를 처음부터 상세하게 문서화하면 부담이 커질 수도 있습니다.&lt;/p&gt;

&lt;p&gt;이번 프로젝트에서는 운영에 필요한 주요 업무와 공통 API부터 REST Docs로 남기고, 개발 중 확인은 Swagger를 활용하는 방향을 선택했습니다. 도구를 하나로 통일하기보다 사용하는 시점과 목적을 구분했습니다.&lt;/p&gt;

&lt;h2 id=&quot;두-문서가-서로-달라지지-않게-해야-했습니다&quot;&gt;두 문서가 서로 달라지지 않게 해야 했습니다&lt;/h2&gt;

&lt;p&gt;두 도구를 함께 사용하면 문서가 두 벌이 되는 문제가 생길 수 있습니다.&lt;/p&gt;

&lt;p&gt;Swagger에는 새로운 필드가 있는데 REST Docs에는 없거나, 테스트 문서는 통과했지만 실제 개발 서버의 설정이 다른 상황도 발생할 수 있습니다. 도구를 추가하는 것보다 변경 흐름을 함께 관리하는 일이 중요했습니다.&lt;/p&gt;

&lt;p&gt;API 변경 시 구현 코드와 테스트, Swagger 설명을 같은 작업에서 수정하도록 했습니다. 공통 응답과 예외 구조도 프로젝트 기준으로 먼저 맞췄습니다.&lt;/p&gt;

&lt;p&gt;CI 과정에서는 테스트와 REST Docs 생성을 함께 수행하고, Swagger는 실제 개발 환경에서 호출 가능한 주소와 인증 방식을 제공하도록 역할을 나눴습니다.&lt;/p&gt;

&lt;h2 id=&quot;api-문서화는-개발-환경의-일부였습니다&quot;&gt;API 문서화는 개발 환경의 일부였습니다&lt;/h2&gt;

&lt;p&gt;Swagger와 REST Docs 설정은 눈에 보이는 기능을 만드는 작업은 아니었습니다. 하지만 이후 여러 개발자가 같은 방식으로 API를 만들고 확인할 수 있는 기반이 되었습니다.&lt;/p&gt;

&lt;p&gt;API 문서는 완성된 결과를 나중에 정리하는 산출물만은 아니었습니다. 개발 중에는 서로의 이해를 맞추고, 변경 시에는 무엇이 달라졌는지 확인하며, 테스트에서는 약속이 지켜졌는지를 검증하는 기준이었습니다.&lt;/p&gt;

&lt;p&gt;두 도구를 함께 가져간 이유는 더 많은 문서를 만들기 위해서가 아닙니다. 빠르게 확인해야 하는 순간과 검증된 기록이 필요한 순간이 서로 달랐기 때문입니다.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>JSP 레거시 전환은 화면을 바꾸는 일이 아니었다</title>
    <link href="https://blog.archilog.dev/posts/jsp-modernization-beyond-ui/"/>
    <updated>2026-08-10T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/jsp-modernization-beyond-ui</id>
    <content type="html">&lt;p&gt;JSP 기반 업무를 React 화면으로 바꾸는 프로젝트를 준비하면서 처음에는 화면 전환이 가장 큰 일이라고 생각했습니다.&lt;/p&gt;

&lt;p&gt;기존 JSP에서 HTML과 스크립트를 분리하고, React로 화면을 다시 만들고, Spring Boot API를 연결하면 새로운 구조로 옮길 수 있을 것처럼 보였습니다.&lt;/p&gt;

&lt;p&gt;하지만 기존 업무를 하나씩 살펴보니 화면 뒤에 더 많은 것이 묶여 있었습니다.&lt;/p&gt;

&lt;h2 id=&quot;jsp-안에는-화면보다-많은-역할이-있었습니다&quot;&gt;JSP 안에는 화면보다 많은 역할이 있었습니다&lt;/h2&gt;

&lt;p&gt;기존 JSP는 화면만 렌더링하는 파일이 아니었습니다.&lt;/p&gt;

&lt;p&gt;세션에서 사용자와 계좌 정보를 읽고, 공통 모듈을 호출하고, 원장 응답을 화면 형식으로 바꾸고, 조건에 따라 다음 화면으로 이동시키는 역할이 함께 들어 있었습니다.&lt;/p&gt;

&lt;p&gt;화면을 React로 바꾸더라도 이 책임들이 사라지는 것은 아니었습니다. 오히려 기존에는 한곳에 숨어 있던 책임을 어디로 옮길지 명확하게 정해야 했습니다.&lt;/p&gt;

&lt;p&gt;어떤 판단은 프론트엔드가 담당하고, 어떤 검증은 API가 책임질지 살펴봤습니다. 앱의 로그인 상태와 WebView 진입 흐름도 함께 봐야 했습니다.&lt;/p&gt;

&lt;h2 id=&quot;화면이-아니라-업무-흐름을-기준으로-봤습니다&quot;&gt;화면이 아니라 업무 흐름을 기준으로 봤습니다&lt;/h2&gt;

&lt;p&gt;전환 대상을 화면 목록으로만 정리하면 비슷해 보이는 화면을 같은 방식으로 옮기기 쉽습니다.&lt;/p&gt;

&lt;p&gt;하지만 조회 업무와 신청·처리 업무는 필요한 기준이 달랐습니다. 조회 화면은 데이터를 안정적으로 제공하고 상태를 표현하는 일이 중심이지만, 처리 업무에는 계좌 상태와 거래 가능 여부, 인증과 전자서명, 중복 요청 방지 같은 검증이 추가됩니다.&lt;/p&gt;

&lt;p&gt;그래서 이번 프로젝트에서는 화면 수보다 업무 흐름을 먼저 정리했습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;진입 전에 필요한 인증 정보는 무엇인지&lt;/li&gt;
  &lt;li&gt;화면에서 조회하는 데이터와 처리에 사용하는 데이터가 같은지&lt;/li&gt;
  &lt;li&gt;서버가 다시 검증해야 하는 조건은 무엇인지&lt;/li&gt;
  &lt;li&gt;처리 후 앱과 웹이 어디로 이동해야 하는지&lt;/li&gt;
  &lt;li&gt;기존 공통 모듈 중 유지하거나 새로 분리할 것은 무엇인지&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 기준을 확인한 뒤에야 화면과 API의 경계를 나눌 수 있었습니다.&lt;/p&gt;

&lt;h2 id=&quot;모든-것을-한-번에-바꾸지-않기로-했습니다&quot;&gt;모든 것을 한 번에 바꾸지 않기로 했습니다&lt;/h2&gt;

&lt;p&gt;기존 구조를 분석하다 보면 공통 기능을 모두 새로 만들고 싶어집니다. 인증, 응답, 예외, 메시지, 원장 연동을 이상적인 모습으로 한 번에 정리하고 싶은 마음도 생깁니다.&lt;/p&gt;

&lt;p&gt;하지만 실제 프로젝트에서는 기존 서비스가 계속 운영되고 있고 일정도 정해져 있습니다. 한 번에 너무 많은 책임을 바꾸면 전환 범위뿐 아니라 검증해야 할 범위도 함께 커집니다.&lt;/p&gt;

&lt;p&gt;이번에는 업무 단위로 전환하되 공통 응답, 인증 컨텍스트, 예외 처리처럼 앞으로 반복해서 사용할 기준부터 만들기로 했습니다. 도메인별 기능은 분리하되 초기 배포 구조는 지나치게 복잡하게 만들지 않는 방향을 선택했습니다.&lt;/p&gt;

&lt;h2 id=&quot;전환의-기준이-달라졌습니다&quot;&gt;전환의 기준이 달라졌습니다&lt;/h2&gt;

&lt;p&gt;JSP 레거시 전환은 오래된 화면을 새로운 프레임워크로 다시 만드는 일이 아니었습니다.&lt;/p&gt;

&lt;p&gt;기존 화면 안에 섞여 있던 책임을 드러내고, 앱과 웹과 서버가 각자 무엇을 맡을지 다시 정하는 일이었습니다. 기술을 바꾸는 것보다 업무 흐름을 잃지 않으면서 다음 업무도 같은 기준으로 만들 수 있게 하는 것이 더 중요했습니다.&lt;/p&gt;

&lt;p&gt;화면은 전환의 결과로 바뀝니다. 하지만 전환을 가능하게 만드는 것은 화면 뒤의 책임을 다시 바라보는 일이라고 생각합니다.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>전자서명에서 nonce와 transactionId가 필요한 이유</title>
    <link href="https://blog.archilog.dev/posts/nonce-and-transaction-id-for-signatures/"/>
    <updated>2026-08-03T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/nonce-and-transaction-id-for-signatures</id>
    <content type="html">&lt;p&gt;전자서명 결과가 유효하다는 사실만으로 업무를 처리할 수 있을지 검토하다가 한 가지 문제가 남았습니다.&lt;/p&gt;

&lt;p&gt;서명 원문과 업무 요청이 같고 서명 자체도 정상이라면 충분해 보입니다. 하지만 이전에 정상적으로 생성된 서명 결과가 다시 전달된다면 서버는 이를 어떻게 구분할 수 있을까요.&lt;/p&gt;

&lt;p&gt;유효한 서명인지 확인하는 것과 지금 처리하려는 요청을 위해 만들어진 서명인지 확인하는 것은 다른 문제였습니다.&lt;/p&gt;

&lt;h2 id=&quot;같은-내용에는-같은-서명을-다시-사용할-수-있었습니다&quot;&gt;같은 내용에는 같은 서명을 다시 사용할 수 있었습니다&lt;/h2&gt;

&lt;p&gt;서명 대상이 계좌, 상품, 금액 같은 업무 데이터로만 구성되어 있다면 같은 조건의 요청은 동일한 형태를 가질 수 있습니다.&lt;/p&gt;

&lt;p&gt;서버가 서명의 유효성과 데이터 일치 여부만 확인하면 과거 요청에서 생성된 서명이 다시 사용되는 상황을 구분하기 어렵습니다.&lt;/p&gt;

&lt;p&gt;업무가 이미 처리되었는지 별도로 확인할 수 있더라도, 모든 업무가 동일한 중복 처리 기준을 가지는 것은 아닙니다. 서명 단계 자체에서 현재 요청과의 관계를 식별할 수 있어야 했습니다.&lt;/p&gt;

&lt;h2 id=&quot;transactionid로-업무-흐름을-연결했습니다&quot;&gt;transactionId로 업무 흐름을 연결했습니다&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transactionId&lt;/code&gt;는 서명 요청부터 실제 업무 처리까지 하나의 흐름을 식별하는 값입니다.&lt;/p&gt;

&lt;p&gt;서버는 업무 요청을 시작할 때 식별자를 발급하고 서명 대상 데이터와 연결합니다. 전자서명 결과가 돌아오면 같은 식별자에 속한 요청인지 확인합니다.&lt;/p&gt;

&lt;p&gt;이 식별자를 기준으로 요청 상태도 관리할 수 있습니다. 서명 대기, 서명 완료, 업무 처리 완료처럼 현재 단계가 어디인지 확인하고 이미 완료된 요청의 재처리를 막을 수 있습니다.&lt;/p&gt;

&lt;p&gt;단순한 화면 추적용 값이 아니라 서버가 현재 업무의 생명주기를 판단하는 기준으로 사용해야 의미가 있었습니다.&lt;/p&gt;

&lt;h2 id=&quot;nonce는-서명-원문을-요청마다-다르게-만들었습니다&quot;&gt;nonce는 서명 원문을 요청마다 다르게 만들었습니다&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nonce&lt;/code&gt;는 한 번의 요청을 위해 생성하는 예측하기 어려운 값입니다.&lt;/p&gt;

&lt;p&gt;같은 업무 데이터라 하더라도 nonce를 서명 대상에 포함하면 요청마다 다른 원문이 만들어집니다. 이전 서명 결과를 새로운 요청에 그대로 사용하는 것을 어렵게 만들 수 있습니다.&lt;/p&gt;

&lt;p&gt;서버는 자신이 발급한 nonce인지 확인하고, 서명 검증이 성공한 뒤에는 다시 사용할 수 없도록 처리합니다. 유효시간을 함께 두면 오래된 요청이 뒤늦게 처리되는 상황도 제한할 수 있습니다.&lt;/p&gt;

&lt;p&gt;nonce를 클라이언트가 임의로 생성하거나 전달한 값을 그대로 신뢰하면 재사용 방지 기준이 약해질 수 있습니다. 생성과 사용 여부 판단은 서버가 책임지는 편이 현재 구조에 맞았습니다.&lt;/p&gt;

&lt;h2 id=&quot;발급-사용-만료를-함께-관리해야-했습니다&quot;&gt;발급, 사용, 만료를 함께 관리해야 했습니다&lt;/h2&gt;

&lt;p&gt;식별자를 추가하는 것만으로 재사용 방지가 완성되지는 않습니다.&lt;/p&gt;

&lt;p&gt;서버에는 어떤 업무 데이터에 발급했는지, 사용되었는지, 유효시간이 지났는지 확인할 상태가 필요합니다. 동시에 들어온 두 요청이 같은 값을 사용하는 상황도 원자적으로 막아야 합니다.&lt;/p&gt;

&lt;p&gt;업무 처리에 실패했을 때 같은 서명을 다시 허용할지도 정해야 합니다. 기술적으로 재시도할 수 있다는 이유만으로 무제한 허용하기보다 업무의 특성과 고객 흐름에 맞는 정책이 필요했습니다.&lt;/p&gt;

&lt;h2 id=&quot;유효한-서명을-현재-요청과-연결하는-기준&quot;&gt;유효한 서명을 현재 요청과 연결하는 기준&lt;/h2&gt;

&lt;p&gt;전자서명의 목적은 서명 결과를 받는 데서 끝나지 않았습니다.&lt;/p&gt;

&lt;p&gt;그 결과가 어떤 업무를 위해, 언제 만들어졌고, 이미 사용되지는 않았는지 설명할 수 있어야 했습니다.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transactionId&lt;/code&gt;는 서명과 업무 처리의 흐름을 연결하고, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nonce&lt;/code&gt;는 요청마다 고유한 서명 원문을 만드는 데 도움을 줍니다.&lt;/p&gt;

&lt;p&gt;이 두 값은 보안을 위한 부가 필드가 아니라 유효한 서명을 현재의 단 한 번의 업무 요청과 연결하기 위한 기준이었습니다.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>WebView의 전자서명은 어디까지 믿을 수 있을까</title>
    <link href="https://blog.archilog.dev/posts/how-far-can-we-trust-webview-signatures/"/>
    <updated>2026-07-27T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/how-far-can-we-trust-webview-signatures</id>
    <content type="html">&lt;p&gt;WebView에서 전자서명을 연동하는 구조를 처음 검토했을 때는 비교적 단순하게 생각했습니다.&lt;/p&gt;

&lt;p&gt;웹에서 서명할 데이터를 만들고 앱 브릿지를 호출합니다. 앱이 전자서명을 수행해 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;signedData&lt;/code&gt;를 돌려주면 웹이 서버로 전달하고, 서버에서 서명을 검증하면 된다고 보았습니다.&lt;/p&gt;

&lt;p&gt;그런데 구조를 계속 들여다보면서 몇 가지 질문이 남았습니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;앱에서 서명에 성공했다면 정말 끝일까?&lt;/p&gt;

  &lt;p&gt;사용자가 서명한 내용과 서버가 처리할 요청은 같은 데이터일까?&lt;/p&gt;

  &lt;p&gt;서버는 클라이언트가 전달한 값 중 어디까지 믿을 수 있을까?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;서명-성공과-업무-검증은-달랐습니다&quot;&gt;서명 성공과 업무 검증은 달랐습니다&lt;/h2&gt;

&lt;p&gt;전자서명 검증이 성공했다는 것은 주어진 원문에 대해 유효한 서명이 생성되었다는 의미입니다.&lt;/p&gt;

&lt;p&gt;하지만 그 원문이 서버가 실제로 처리할 업무 요청과 같은지는 별개의 문제였습니다.&lt;/p&gt;

&lt;p&gt;WebView에서 생성한 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plainData&lt;/code&gt;, 앱에서 만들어진 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;signedData&lt;/code&gt;, 서버로 전달되는 업무 요청 데이터는 서로 다른 경로를 지나갑니다. 세 값이 언제나 같을 것이라고 가정하면 중간 구간에서 값이 달라지는 상황을 확인하기 어렵습니다.&lt;/p&gt;

&lt;p&gt;앱 브릿지는 웹과 앱을 연결하는 중요한 통로지만, 브릿지를 통과했다는 사실만으로 업무 데이터의 신뢰성이 완성되는 것은 아니었습니다.&lt;/p&gt;

&lt;h2 id=&quot;원문을-어디에서-만들-것인지부터-다시-보았습니다&quot;&gt;원문을 어디에서 만들 것인지부터 다시 보았습니다&lt;/h2&gt;

&lt;p&gt;WebView가 서명 원문을 모두 만드는 방식은 화면 구현이 빠르고 유연합니다.&lt;/p&gt;

&lt;p&gt;반면 서버가 모르는 형태의 원문이 만들어질 수 있고, 실제 업무 요청과 비교하기 위해 같은 조립 규칙을 서버에도 유지해야 합니다. 화면마다 원문 형식이 달라지면 검증 기준도 복잡해집니다.&lt;/p&gt;

&lt;p&gt;서버가 업무 요청을 기준으로 서명 대상 데이터를 만들고 식별자를 발급하는 방식은 신뢰 기준을 서버에 둘 수 있습니다. 다만 서명 전후의 흐름과 상태 관리가 추가되고, 화면과 서버 사이의 계약을 더 명확하게 정의해야 합니다.&lt;/p&gt;

&lt;p&gt;이번 프로젝트에서는 어느 위치에서 원문을 만들든 서버가 최종 비교 기준을 가져야 한다고 보았습니다.&lt;/p&gt;

&lt;h2 id=&quot;서버가-다시-확인해야-할-것&quot;&gt;서버가 다시 확인해야 할 것&lt;/h2&gt;

&lt;p&gt;서버는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;signedData&lt;/code&gt;의 암호학적 유효성만 확인해서는 충분하지 않았습니다.&lt;/p&gt;

&lt;p&gt;검증을 통해 얻은 서명 원문이 서버가 기대한 원문과 같은지 확인해야 합니다. 계좌, 상품, 금액, 업무 구분처럼 실제 처리에 영향을 주는 값이 서명 대상과 업무 요청에서 일치하는지도 비교해야 합니다.&lt;/p&gt;

&lt;p&gt;서명이 현재 요청을 위해 발급된 것인지, 이미 사용된 요청이 아닌지, 허용된 시간 안에 처리되는지도 함께 봐야 합니다.&lt;/p&gt;

&lt;p&gt;클라이언트가 전달한 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plainData&lt;/code&gt;를 비교 기준으로 다시 신뢰하는 것이 아니라 서버가 보관하거나 재구성할 수 있는 값과 비교하는 것이 필요했습니다.&lt;/p&gt;

&lt;h2 id=&quot;전자서명을-하나의-흐름으로-보게-되었습니다&quot;&gt;전자서명을 하나의 흐름으로 보게 되었습니다&lt;/h2&gt;

&lt;p&gt;전자서명은 브릿지 호출의 성공 여부를 확인하는 기능이 아니었습니다.&lt;/p&gt;

&lt;p&gt;사용자가 확인한 내용, 실제로 서명한 원문, 서버가 처리할 업무 요청이 하나의 흐름으로 이어지고 있는지 검증하는 일이었습니다.&lt;/p&gt;

&lt;p&gt;앱은 안전한 서명 기능을 제공하고, 웹은 사용자에게 내용을 보여주며 서명 흐름을 연결합니다. 서버는 전달받은 결과를 기반으로 실제 업무를 처리해도 되는지 최종 판단합니다.&lt;/p&gt;

&lt;p&gt;전자서명을 어디까지 믿을 수 있는지에 대한 답은 특정 구간을 신뢰하는 데 있지 않았습니다.&lt;/p&gt;

&lt;p&gt;각 구간이 만든 결과를 서버의 업무 기준으로 다시 연결해 확인할 수 있을 때, 비로소 서명과 업무 처리가 같은 요청이었다고 말할 수 있었습니다.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>로그인 사용자는 바로 업무로, 비로그인 사용자는 설명 화면으로</title>
    <link href="https://blog.archilog.dev/posts/webview-flow-by-login-state/"/>
    <updated>2026-07-20T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/webview-flow-by-login-state</id>
    <content type="html">&lt;p&gt;앱 안에서 업무 화면을 제공할 때는 로그인한 고객만 생각하기 쉽습니다.&lt;/p&gt;

&lt;p&gt;업무 버튼을 누르면 WebView를 열고 바로 업무 화면을 보여주면 된다고 생각했습니다. 하지만 같은 주소가 외부 링크나 다른 화면을 통해 전달될 수 있다면 비로그인 상태의 진입도 함께 고려해야 했습니다.&lt;/p&gt;

&lt;p&gt;로그인이 필요한 화면이라고 해서 비로그인 고객을 오류 화면이나 빈 화면에 머물게 할 수는 없었습니다.&lt;/p&gt;

&lt;h2 id=&quot;url을-나누는-방법부터-생각했습니다&quot;&gt;URL을 나누는 방법부터 생각했습니다&lt;/h2&gt;

&lt;p&gt;처음에는 로그인 사용자용 주소와 비로그인 사용자용 주소를 따로 두는 방식을 생각할 수 있었습니다.&lt;/p&gt;

&lt;p&gt;구현은 명확해 보이지만 링크를 만드는 쪽에서 사용자의 로그인 상태를 알아야 합니다. 로그인 상태가 바뀌면 어느 주소로 돌아가야 하는지도 별도로 관리해야 합니다.&lt;/p&gt;

&lt;p&gt;같은 업무를 설명하는 페이지와 실제 업무 페이지가 서로 다른 진입 경로를 가지면 공유 링크와 앱 화면 이동 규칙도 복잡해질 수 있습니다.&lt;/p&gt;

&lt;p&gt;중요한 것은 Endpoint를 나누는 것이 아니라 하나의 진입 경로 안에서 사용자의 상태에 맞는 다음 화면을 자연스럽게 결정하는 일이었습니다.&lt;/p&gt;

&lt;h2 id=&quot;같은-url에서-첫-화면을-결정했습니다&quot;&gt;같은 URL에서 첫 화면을 결정했습니다&lt;/h2&gt;

&lt;p&gt;이번 프로젝트에서는 업무별 진입 URL을 하나로 유지하는 방향을 검토했습니다.&lt;/p&gt;

&lt;p&gt;로그인한 사용자는 별도의 안내를 반복해서 보지 않고 바로 업무 화면으로 이동합니다. 이미 서비스 이용 의도가 분명한 고객에게 불필요한 단계를 추가하지 않기 위해서입니다.&lt;/p&gt;

&lt;p&gt;비로그인 사용자는 업무의 목적과 이용 조건을 설명하는 화면을 먼저 봅니다. 여기에서 로그인을 선택하면 앱의 로그인 기능을 호출하고, 성공한 뒤 원래 진입했던 업무로 돌아옵니다.&lt;/p&gt;

&lt;p&gt;설명 화면은 단순히 로그인 버튼을 보여주는 곳이 아닙니다. 사용자가 어떤 업무에 들어가려 했는지 이해하고, 로그인 이후 무엇을 할 수 있는지 알려주는 진입 화면에 가깝습니다.&lt;/p&gt;

&lt;h2 id=&quot;로그인-후-복귀-정보가-필요했습니다&quot;&gt;로그인 후 복귀 정보가 필요했습니다&lt;/h2&gt;

&lt;p&gt;로그인 호출 자체보다 더 고민이 필요했던 부분은 로그인 이후였습니다.&lt;/p&gt;

&lt;p&gt;로그인이 끝난 뒤 WebView를 새로 열지, 기존 화면을 갱신할지, 사용자가 처음 요청했던 업무와 파라미터를 어떻게 이어갈지 기준이 필요했습니다.&lt;/p&gt;

&lt;p&gt;복귀 정보는 클라이언트 화면 상태에만 의존하지 않도록 범위를 제한하고, 허용된 업무 경로인지 확인해야 합니다. 로그인 과정이 길어지거나 사용자가 취소했을 때의 동작도 함께 정해야 했습니다.&lt;/p&gt;

&lt;p&gt;이번 구조에서는 로그인 성공, 취소, 실패를 구분하고 성공한 경우에만 원래 업무 진입을 다시 판단하도록 했습니다. 로그인 전 화면이 업무 처리 상태를 임의로 이어가지 않도록 하는 것이 중요했습니다.&lt;/p&gt;

&lt;h2 id=&quot;인증과-고객-경험은-같은-흐름이었습니다&quot;&gt;인증과 고객 경험은 같은 흐름이었습니다&lt;/h2&gt;

&lt;p&gt;로그인 여부를 확인하는 일은 보안 처리처럼 보이지만 고객이 경험하는 화면 흐름과도 맞닿아 있었습니다.&lt;/p&gt;

&lt;p&gt;인증이 필요하다는 이유로 사용자의 목적을 끊어버리면 다시 메뉴를 찾아야 합니다. 반대로 편리한 복귀만 생각해 검증되지 않은 경로를 그대로 이어주면 안전한 흐름이라고 보기 어렵습니다.&lt;/p&gt;

&lt;p&gt;하나의 URL을 유지한 이유는 구조를 단순하게 만들기 위해서만은 아니었습니다.&lt;/p&gt;

&lt;p&gt;고객이 어떤 상태로 들어오더라도 자신이 하려던 일을 이해하고, 필요한 인증을 거쳐 자연스럽게 이어갈 수 있게 하는 것. 로그인 분기는 인증과 사용자 경험을 하나의 흐름으로 보는 데서 시작했습니다.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>GNB는 앱 영역인가, 웹 영역인가</title>
    <link href="https://blog.archilog.dev/posts/is-gnb-app-or-web/"/>
    <updated>2026-07-13T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/is-gnb-app-or-web</id>
    <content type="html">&lt;p&gt;하이브리드 앱의 업무 화면을 준비하면서 하단 GNB를 어떻게 처리할지 논의하게 되었습니다.&lt;/p&gt;

&lt;p&gt;처음에는 GNB가 앱 화면에 이미 존재하니 웹에서는 크게 신경 쓰지 않아도 될 것처럼 보였습니다.&lt;/p&gt;

&lt;p&gt;하지만 WebView를 전체 화면으로 띄우고 앱 GNB를 그 위에 노출하는 구조에서는 웹도 GNB의 존재를 알아야 했습니다. 그렇지 않으면 웹 콘텐츠의 마지막 영역이 GNB 뒤에 가려질 수 있었습니다.&lt;/p&gt;

&lt;h2 id=&quot;앱에서-그리지만-웹-레이아웃에도-영향을-주었습니다&quot;&gt;앱에서 그리지만 웹 레이아웃에도 영향을 주었습니다&lt;/h2&gt;

&lt;p&gt;이번 구조에서는 GNB를 앱 영역으로 유지했습니다.&lt;/p&gt;

&lt;p&gt;앱 전체에서 동일한 메뉴 경험을 제공하고, 앱 화면과 WebView 화면 사이에서도 일관된 동작을 유지하기 위한 선택이었습니다.&lt;/p&gt;

&lt;p&gt;대신 웹 화면에는 GNB 높이만큼 하단 여백을 추가했습니다. 이 여백은 디자인을 위한 공간이 아니라 GNB가 웹 콘텐츠 위를 덮어도 마지막 내용이 잘리지 않게 하기 위한 영역입니다.&lt;/p&gt;

&lt;p&gt;GNB가 없는 화면에서는 불필요한 여백이 생기지 않도록 웹에서도 옵션으로 적용 여부를 선택할 수 있게 했습니다.&lt;/p&gt;

&lt;h2 id=&quot;바텀시트가-올라오면-gnb는-어떻게-해야-할까&quot;&gt;바텀시트가 올라오면 GNB는 어떻게 해야 할까&lt;/h2&gt;

&lt;p&gt;레이아웃을 맞추고 나니 동작에 대한 질문이 이어졌습니다.&lt;/p&gt;

&lt;p&gt;웹 바텀시트가 화면 아래에서 올라올 때 GNB가 그대로 남아 있으면 두 개의 하단 UI가 겹칩니다. 바텀시트가 GNB 위에서 멈추게 할 수도 있고, GNB를 숨기고 바텀시트를 화면 끝까지 올릴 수도 있습니다.&lt;/p&gt;

&lt;p&gt;이번 프로젝트에서는 바텀시트가 올라올 때 앱 GNB가 아래로 내려가고, 바텀시트가 닫힐 때 다시 올라오는 흐름을 검토했습니다.&lt;/p&gt;

&lt;p&gt;웹은 바텀시트의 상태를 알고 있고 앱은 GNB를 제어합니다. 두 영역이 함께 움직여야 하므로 상태를 전달하는 기준과 애니메이션 타이밍을 맞추는 일이 필요했습니다.&lt;/p&gt;

&lt;h2 id=&quot;스크롤은-앱에서-감지하기로-했습니다&quot;&gt;스크롤은 앱에서 감지하기로 했습니다&lt;/h2&gt;

&lt;p&gt;스크롤이 긴 화면에서는 콘텐츠에 집중할 수 있도록 GNB를 숨기는 동작도 필요했습니다.&lt;/p&gt;

&lt;p&gt;웹이 스크롤 방향을 계산해 매번 앱에 전달하는 방법도 생각할 수 있습니다. 하지만 화면마다 같은 감지 로직을 구현하면 기준이 달라질 수 있고 Bridge 호출도 반복됩니다.&lt;/p&gt;

&lt;p&gt;현재 구조에서는 앱이 WebView의 스크롤을 감지해 GNB의 노출과 숨김을 처리하는 방향으로 정리했습니다. 웹 화면은 GNB의 존재를 고려한 여백과 노출 옵션을 담당하고, 실제 스크롤 동작은 앱이 일관되게 처리합니다.&lt;/p&gt;

&lt;h2 id=&quot;모달의-딤-처리는-남은-경계였습니다&quot;&gt;모달의 딤 처리는 남은 경계였습니다&lt;/h2&gt;

&lt;p&gt;웹에서 모달을 띄우고 WebView 내부만 딤 처리하면 앱 GNB는 어두워지지 않습니다.&lt;/p&gt;

&lt;p&gt;모달 하나를 위해 매번 Bridge를 연결하면 웹 UI의 독립성이 줄어들 수 있습니다. 반대로 아무 처리도 하지 않으면 화면 전체가 하나의 레이어처럼 보이지 않을 수 있습니다.&lt;/p&gt;

&lt;p&gt;이 부분은 실제 구현 결과를 확인한 뒤 다시 판단하기로 했습니다. 모든 경계를 처음부터 복잡하게 연결하기보다 사용자 경험에 문제가 되는지 먼저 확인하는 것도 필요한 기준이라고 생각했습니다.&lt;/p&gt;

&lt;p&gt;GNB는 앱에서 그리는 UI이지만 웹과 분리된 영역은 아니었습니다.&lt;/p&gt;

&lt;p&gt;하이브리드 앱에서 앱과 웹의 경계는 누가 화면을 그리는지만으로 정해지지 않습니다. 서로의 존재가 레이아웃과 동작에 어떤 영향을 주는지 함께 정할 때 비로소 하나의 화면처럼 움직일 수 있었습니다.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>하이브리드 앱에서 Native Bridge 사용 기준에 대한 고민</title>
    <link href="https://blog.archilog.dev/posts/native-bridge-usage-guidelines/"/>
    <updated>2026-07-06T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/native-bridge-usage-guidelines</id>
    <content type="html">&lt;p&gt;WebView 화면에서 앱 기능이 필요할 때 Native Bridge를 직접 호출하는 방식은 처음에는 가장 단순해 보입니다.&lt;/p&gt;

&lt;p&gt;앱에서 제공하는 함수 이름과 파라미터를 확인하고 화면에서 호출하면 됩니다. 토스트 메시지, 앱 화면 이동, WebView 닫기처럼 작은 기능은 이 방식만으로도 빠르게 연결할 수 있습니다.&lt;/p&gt;

&lt;p&gt;하지만 화면이 늘면서 직접 호출 방식이 조금씩 부담으로 바뀌기 시작했습니다.&lt;/p&gt;

&lt;h2 id=&quot;화면마다-앱-환경을-알아야-했습니다&quot;&gt;화면마다 앱 환경을 알아야 했습니다&lt;/h2&gt;

&lt;p&gt;Bridge를 직접 사용하는 화면은 앱에서 제공하는 함수 이름과 파라미터 구조를 알아야 합니다.&lt;/p&gt;

&lt;p&gt;브라우저에서 단독으로 실행하면 해당 함수가 없기 때문에 예외 처리도 필요합니다. 앱 버전에 따라 지원 여부가 다르다면 버전 확인과 대체 동작까지 화면에서 판단해야 합니다.&lt;/p&gt;

&lt;p&gt;비슷한 기능인데도 화면마다 호출 방식이나 오류 처리가 달라질 가능성이 생겼습니다. 앱의 인터페이스가 바뀌면 여러 화면을 함께 수정해야 하는 문제도 있었습니다.&lt;/p&gt;

&lt;p&gt;중요한 것은 Bridge를 호출할 수 있느냐가 아니라, 웹서비스 안에서 Bridge를 어떤 기준으로 사용할 것인가였습니다.&lt;/p&gt;

&lt;h2 id=&quot;공통-wrapper를-두기로-했습니다&quot;&gt;공통 Wrapper를 두기로 했습니다&lt;/h2&gt;

&lt;p&gt;이번 프로젝트에서는 화면과 Native Bridge 사이에 공통 Wrapper를 두었습니다.&lt;/p&gt;

&lt;p&gt;화면은 앱의 실제 함수 이름보다 웹서비스에서 정의한 공통 기능을 사용합니다. Wrapper는 현재 앱 환경인지 확인하고, 필요한 파라미터를 구성한 뒤 Bridge를 호출합니다.&lt;/p&gt;

&lt;p&gt;브라우저에서 실행할 때는 기능별 fallback을 제공합니다. 단순 메시지는 웹 토스트로 대체하고, 앱 화면 이동처럼 브라우저에서 수행할 수 없는 기능은 안전하게 종료하거나 개발자가 확인할 수 있는 방식으로 처리합니다.&lt;/p&gt;

&lt;p&gt;앱 버전별 지원 여부와 공통 오류 처리도 Wrapper에 모았습니다. 화면 개발자는 앱 내부 구현보다 자신이 요청하는 기능과 결과에 집중할 수 있게 됩니다.&lt;/p&gt;

&lt;h2 id=&quot;wrapper가-모든-차이를-숨기지는-않습니다&quot;&gt;Wrapper가 모든 차이를 숨기지는 않습니다&lt;/h2&gt;

&lt;p&gt;공통 모듈을 만들었다고 앱과 웹의 차이가 사라지는 것은 아닙니다.&lt;/p&gt;

&lt;p&gt;전자서명이나 로그인처럼 사용자 흐름과 보안 검증이 중요한 기능은 단순한 함수 호출로 추상화하기 어렵습니다. 호출 결과뿐 아니라 취소, 실패, 중복 호출, 화면 복귀까지 계약으로 정의해야 합니다.&lt;/p&gt;

&lt;p&gt;그래서 Wrapper의 목적을 앱 기능을 무조건 같은 모습으로 만드는 것으로 두지 않았습니다. 화면마다 반복하지 않아도 될 환경 판단과 호출 규칙을 모으고, 중요한 차이는 명시적인 인터페이스로 드러내는 쪽에 가깝습니다.&lt;/p&gt;

&lt;h2 id=&quot;브릿지를-연결하는-일에서-기준을-제공하는-일로&quot;&gt;브릿지를 연결하는 일에서 기준을 제공하는 일로&lt;/h2&gt;

&lt;p&gt;Native Bridge는 앱과 웹을 이어주는 통로입니다.&lt;/p&gt;

&lt;p&gt;통로가 있다는 사실만으로 웹서비스가 일관되게 동작하는 것은 아니었습니다. 어떤 기능을 공통화하고, 지원하지 않는 환경에서 어떻게 행동하며, 오류를 어디까지 화면에 전달할지 함께 정해야 했습니다.&lt;/p&gt;

&lt;p&gt;Bridge Wrapper는 단순한 유틸리티보다 앱과 웹이 합의한 사용 기준에 가까웠습니다.&lt;/p&gt;

&lt;p&gt;화면 개발자가 앱 연동 방식이 아니라 고객에게 제공할 업무 흐름에 집중할 수 있게 하는 것. 그것이 이번 프로젝트에서 Wrapper를 만든 가장 큰 이유였습니다.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>앱 안에서 웹서비스를 자연스럽게 제공하려면 무엇을 먼저 정해야 할까</title>
    <link href="https://blog.archilog.dev/posts/what-to-define-first-for-webview-service/"/>
    <updated>2026-06-29T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/what-to-define-first-for-webview-service</id>
    <content type="html">&lt;p&gt;앱 안에서 새로운 웹서비스를 제공하는 일을 처음 검토했을 때는 구조가 비교적 단순해 보였습니다.&lt;/p&gt;

&lt;p&gt;앱에서 WebView를 열고, 웹에서 화면을 만들고, 필요한 기능은 브릿지를 통해 앱에 요청하면 된다고 생각했습니다.&lt;/p&gt;

&lt;p&gt;하지만 실제 화면 흐름을 하나씩 살펴보니 기술 스택보다 먼저 정해야 할 것이 있었습니다. 앱과 웹, 서버가 각각 어디까지 책임질 것인지에 대한 기준이었습니다.&lt;/p&gt;

&lt;h2 id=&quot;작은-기능마다-경계가-드러났습니다&quot;&gt;작은 기능마다 경계가 드러났습니다&lt;/h2&gt;

&lt;p&gt;로그인, 화면 이동, 뒤로가기, GNB, 바텀시트, 전자서명은 각각 하나의 기능처럼 보입니다.&lt;/p&gt;

&lt;p&gt;그런데 하이브리드 앱에서는 이 기능들이 앱과 웹의 경계에 걸쳐 있었습니다.&lt;/p&gt;

&lt;p&gt;로그인 화면은 앱에서 제공하지만 웹은 로그인 결과를 이어받아 원래 업무로 돌아가야 합니다. GNB는 앱이 그리지만 웹 콘텐츠가 가려지지 않도록 레이아웃을 맞춰야 합니다. 전자서명은 앱에서 수행하더라도 서버가 실제 업무 요청과 서명 원문을 다시 확인해야 합니다.&lt;/p&gt;

&lt;p&gt;어느 한쪽에서만 결정하면 다른 영역에 예외 처리가 쌓이기 쉬운 구조였습니다.&lt;/p&gt;

&lt;h2 id=&quot;역할이-불분명하면-화면마다-구현이-달라집니다&quot;&gt;역할이 불분명하면 화면마다 구현이 달라집니다&lt;/h2&gt;

&lt;p&gt;기준이 없는 상태에서는 화면 개발자가 필요할 때마다 브릿지를 직접 호출할 수 있습니다.&lt;/p&gt;

&lt;p&gt;어떤 화면은 앱 환경을 가정하고, 다른 화면은 브라우저 실행을 고려합니다. 앱 버전에 따라 지원되지 않는 기능을 각 화면에서 따로 처리할 수도 있습니다.&lt;/p&gt;

&lt;p&gt;처음에는 빠르게 구현할 수 있지만 화면이 늘어날수록 호출 방식과 예외 처리도 함께 늘어납니다. 문제가 발생했을 때 앱과 웹 중 어디에서 확인해야 하는지도 모호해집니다.&lt;/p&gt;

&lt;p&gt;이번 프로젝트에서는 개별 기능보다 먼저 역할을 정리하기로 했습니다.&lt;/p&gt;

&lt;h2 id=&quot;앱-웹-서버의-역할을-나누었습니다&quot;&gt;앱, 웹, 서버의 역할을 나누었습니다&lt;/h2&gt;

&lt;p&gt;앱은 네이티브 기능과 앱 전체의 일관된 사용자 경험을 담당합니다. 로그인, 전자서명, 앱 화면 이동처럼 단말과 앱 환경이 필요한 기능이 여기에 포함됩니다.&lt;/p&gt;

&lt;p&gt;웹은 업무 화면과 콘텐츠를 빠르게 제공하고 변경하는 역할을 맡습니다. 다만 앱 기능을 직접 호출하는 방식은 공통 Wrapper 안으로 감추고, 화면에서는 일관된 인터페이스를 사용하도록 했습니다.&lt;/p&gt;

&lt;p&gt;서버는 클라이언트에서 전달된 결과를 그대로 신뢰하지 않고 업무 처리에 필요한 기준을 다시 검증합니다. 인증 상태와 요청 데이터, 전자서명의 원문 관계처럼 신뢰가 필요한 판단은 서버의 책임으로 두었습니다.&lt;/p&gt;

&lt;p&gt;이 구분이 모든 상황의 정답은 아닙니다. 다만 현재 구조에서는 각 영역이 잘할 수 있는 일과 반드시 책임져야 할 일을 나누는 기준이 되었습니다.&lt;/p&gt;

&lt;h2 id=&quot;기술보다-먼저-합의해야-했던-것&quot;&gt;기술보다 먼저 합의해야 했던 것&lt;/h2&gt;

&lt;p&gt;WebView 서비스는 웹 화면을 앱에 넣는 방식만으로 설명하기 어려웠습니다.&lt;/p&gt;

&lt;p&gt;앱과 웹이 함께 움직이는 순간마다 누가 상태를 관리하고, 누가 사용자 경험을 책임지며, 누가 최종적으로 신뢰를 판단할지 정해야 했습니다.&lt;/p&gt;

&lt;p&gt;이 기준이 먼저 정리되자 Bridge, GNB, 로그인, 전자서명 같은 후속 논의도 조금 더 같은 방향에서 이야기할 수 있었습니다.&lt;/p&gt;

&lt;p&gt;하이브리드 앱에서 가장 먼저 만들어야 하는 것은 화면이 아니라, 앱과 웹과 서버가 함께 지킬 역할의 경계인지도 모르겠습니다.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>채널 개발자는 왜 회의가 많아졌을까</title>
    <link href="https://blog.archilog.dev/posts/why-channel-developers-have-more-meetings/"/>
    <updated>2026-06-22T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/why-channel-developers-have-more-meetings</id>
    <content type="html">&lt;p&gt;어느 날 회의를 마치고 자리에 돌아오니 하루의 많은 시간이 지나 있었습니다.&lt;/p&gt;

&lt;p&gt;예전 같았으면 회의가 이렇게 많은데 개발은 언제 해야 할까 하는 생각부터 들었을 것 같습니다. 실제로 집중해서 코드를 작성할 시간이 줄어드는 것은 여전히 부담입니다.&lt;/p&gt;

&lt;p&gt;그런데 최근에는 회의가 많아진 이유를 조금 다르게 보게 되었습니다.&lt;/p&gt;

&lt;h2 id=&quot;예전에는-정해진-내용을-구현하는-일이-많았습니다&quot;&gt;예전에는 정해진 내용을 구현하는 일이 많았습니다&lt;/h2&gt;

&lt;p&gt;몇 년 전까지만 해도 프로젝트의 순서는 비교적 분명했습니다.&lt;/p&gt;

&lt;p&gt;기획과 디자인이 정리되고 원장이나 코어 서비스의 방향이 어느 정도 확정된 뒤 채널 개발자가 화면을 만들고 API를 연결하는 경우가 많았습니다.&lt;/p&gt;

&lt;p&gt;이때의 중요한 질문은 주어진 요구사항을 어떻게 안정적으로 구현할 것인가였습니다. 개발자는 프로젝트의 뒤쪽에서 완성된 기준을 코드로 바꾸는 역할에 가까웠습니다.&lt;/p&gt;

&lt;p&gt;하지만 앱과 웹, 여러 서버가 함께 움직이는 서비스에서는 구현 전에 정하지 않으면 안 되는 것이 많아졌습니다.&lt;/p&gt;

&lt;h2 id=&quot;경계의-문제는-한-팀이-정하기-어려웠습니다&quot;&gt;경계의 문제는 한 팀이 정하기 어려웠습니다&lt;/h2&gt;

&lt;p&gt;WebView를 어디까지 앱 화면처럼 보이게 할지, GNB와 바텀시트가 함께 움직일 때 누가 상태를 제어할지, 로그인 후 사용자를 어느 화면으로 돌려보낼지 정해야 했습니다.&lt;/p&gt;

&lt;p&gt;전자서명도 앱에서 성공 결과를 받는 것만으로 끝나지 않았습니다. 웹이 만든 원문과 앱이 서명한 데이터, 서버가 실제로 처리할 요청을 어떤 기준으로 비교할지 인증과 앱, 서버 담당자가 함께 봐야 했습니다.&lt;/p&gt;

&lt;p&gt;이런 문제는 각 팀이 자기 영역만 구현한 뒤 연결해서 풀기 어렵습니다. 경계의 기준이 늦게 정해지면 화면마다 다른 방식이 생기고, 이미 만든 기능을 다시 수정해야 할 수 있습니다.&lt;/p&gt;

&lt;p&gt;그래서 채널 개발자가 구현 전에 참여하는 회의가 자연스럽게 늘었습니다.&lt;/p&gt;

&lt;h2 id=&quot;회의도-개발의-일부가-될-수-있었습니다&quot;&gt;회의도 개발의 일부가 될 수 있었습니다&lt;/h2&gt;

&lt;p&gt;모든 회의가 필요한 것은 아닙니다. 결론 없이 현황만 공유하거나 책임을 나누기 위한 회의는 개발 시간을 줄일 뿐입니다.&lt;/p&gt;

&lt;p&gt;반면 역할의 경계를 정하고 다음 구현의 기준을 남기는 회의는 코드 작성 이전의 개발에 가까웠습니다.&lt;/p&gt;

&lt;p&gt;회의가 끝난 뒤 다음 내용이 분명해지는지를 기준으로 보게 되었습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;앱, 웹, 서버가 각각 책임질 범위&lt;/li&gt;
  &lt;li&gt;정상 흐름과 예외 흐름의 처리 주체&lt;/li&gt;
  &lt;li&gt;공통 모듈이나 인터페이스로 남길 내용&lt;/li&gt;
  &lt;li&gt;아직 구현으로 확인해야 하는 가정&lt;/li&gt;
  &lt;li&gt;변경 시 함께 검토해야 할 영역&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;결정이 문서와 개발 요청, 공통 규칙으로 이어진다면 다음 개발자가 같은 문제를 다시 논의하는 시간을 줄일 수 있습니다.&lt;/p&gt;

&lt;h2 id=&quot;역할이-코드-밖으로-조금-넓어졌습니다&quot;&gt;역할이 코드 밖으로 조금 넓어졌습니다&lt;/h2&gt;

&lt;p&gt;채널 개발자의 역할은 화면과 API를 구현하는 데서 끝나지 않았습니다.&lt;/p&gt;

&lt;p&gt;고객이 앱과 웹의 경계를 느끼지 않도록 여러 영역의 기준을 연결하고, 구현 전에 충돌할 수 있는 지점을 찾아 함께 결정하는 일이 늘었습니다.&lt;/p&gt;

&lt;p&gt;회의가 많아졌다는 사실 자체를 긍정적으로 보려는 것은 아닙니다. 다만 필요한 결정을 앞에서 내리는 시간이라면 뒤에서 반복되는 수정과 해석의 차이를 줄일 수 있습니다.&lt;/p&gt;

&lt;p&gt;요즘은 개발 시간을 코드 작성 시간으로만 보지 않게 되었습니다. 여러 팀이 같은 기준에서 구현을 시작할 수 있게 만드는 일도 채널 개발자가 만드는 결과 중 하나라고 생각합니다.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>코드 너머의 채널 개발자</title>
    <link href="https://blog.archilog.dev/posts/code-beyond-channel-developer/"/>
    <updated>2026-06-15T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/code-beyond-channel-developer</id>
    <content type="html">&lt;p&gt;예전에 면접에서 이런 질문을 받은 적이 있습니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“왜 이렇게 다양한 서비스를 담당하게 되었나요?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;당시에는 명확하게 답하지 못했던 것 같습니다. 지나고 보니 이유는 단순했습니다. 채널 서비스를 개발하다 보면 어느 한 영역으로 구분하기 어려운 일이 자연스럽게 모이기 때문입니다.&lt;/p&gt;

&lt;p&gt;고객이 만나는 화면은 앱이나 웹으로 보이지만, 그 뒤에는 인증과 원장 서비스, 공통 API와 운영 시스템이 함께 움직입니다. 어느 한 부분만 잘 만든다고 고객 서비스가 자연스럽게 이어지는 것은 아니었습니다.&lt;/p&gt;

&lt;h2 id=&quot;화면을-만드는-일이라고-생각했습니다&quot;&gt;화면을 만드는 일이라고 생각했습니다&lt;/h2&gt;

&lt;p&gt;처음 채널 개발을 시작했을 때는 주어진 화면을 안정적으로 구현하는 것이 가장 중요한 역할이라고 생각했습니다.&lt;/p&gt;

&lt;p&gt;기획과 디자인을 확인하고, API를 연결하고, 고객이 불편하지 않도록 화면을 만드는 일에 집중했습니다. 물론 지금도 중요한 일입니다.&lt;/p&gt;

&lt;p&gt;하지만 담당하는 서비스가 늘면서 화면 밖의 문제를 더 자주 마주하게 되었습니다.&lt;/p&gt;

&lt;p&gt;로그인 상태는 어디에서 판단할지, 앱과 웹의 화면 이동은 어떻게 맞출지, 원장의 기술적인 메시지는 고객에게 어떻게 보여줄지, 장애가 발생했을 때 어느 구간부터 확인해야 할지 고민해야 했습니다.&lt;/p&gt;

&lt;h2 id=&quot;경계에-있는-문제는-한쪽에서만-풀기-어려웠습니다&quot;&gt;경계에 있는 문제는 한쪽에서만 풀기 어려웠습니다&lt;/h2&gt;

&lt;p&gt;채널에서 마주하는 문제는 앱, 웹, 서버 중 한곳의 문제로만 설명하기 어려운 경우가 많았습니다.&lt;/p&gt;

&lt;p&gt;WebView 안에서 발생한 현상도 앱 브릿지의 문제일 수 있고, 웹의 상태 관리나 서버 응답의 문제일 수 있습니다. 인증과 전자서명 역시 성공 결과를 전달받는 것만으로 끝나지 않고, 실제 업무 요청까지 같은 흐름으로 검증되어야 했습니다.&lt;/p&gt;

&lt;p&gt;이런 문제를 해결하려면 각 영역을 모두 직접 구현하는 것보다 서로의 역할과 경계를 이해하는 일이 먼저였습니다.&lt;/p&gt;

&lt;p&gt;앱이 책임져야 할 것, 웹에서 유연하게 바꿀 것, 서버가 신뢰할 수 있는 기준으로 검증할 것을 함께 정해야 했습니다.&lt;/p&gt;

&lt;h2 id=&quot;회의가-많아진-이유도-조금-다르게-보였습니다&quot;&gt;회의가 많아진 이유도 조금 다르게 보였습니다&lt;/h2&gt;

&lt;p&gt;예전에는 회의가 많아지면 개발할 시간이 줄어든다고만 생각했습니다.&lt;/p&gt;

&lt;p&gt;하지만 프로젝트를 진행하는 방식이 달라지면서 채널 개발자가 더 앞쪽의 논의에 참여해야 하는 일이 많아졌습니다. 기획과 디자인이 모두 확정된 뒤 구현하는 것이 아니라, 앱과 웹의 경계나 인증 흐름처럼 구현 전에 정해야 하는 기준을 함께 이야기하게 되었습니다.&lt;/p&gt;

&lt;p&gt;회의가 많아진 것이 언제나 좋은 것은 아닙니다. 다만 필요한 기준을 미리 맞추는 회의라면 뒤에서 발생할 시행착오를 줄이는 개발의 일부라고 생각하게 되었습니다.&lt;/p&gt;

&lt;h2 id=&quot;그래서-채널-개발이-좋습니다&quot;&gt;그래서 채널 개발이 좋습니다&lt;/h2&gt;

&lt;p&gt;채널 개발은 한 가지 기술을 깊게 다루는 일과 여러 영역을 연결해서 보는 일이 함께 필요합니다.&lt;/p&gt;

&lt;p&gt;때로는 담당 영역이 모호해 보이기도 합니다. 하지만 그 모호한 경계에서 고객 서비스가 끊기지 않도록 기준을 찾는 일이 채널 개발자의 역할이라고 생각합니다.&lt;/p&gt;

&lt;p&gt;코드만으로 해결할 수 없는 문제를 만나고, 여러 영역의 언어를 이해하며 하나의 고객 경험으로 이어지게 만드는 것.&lt;/p&gt;

&lt;p&gt;저는 그래서 채널 개발이 좋습니다.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>기술보다 선택의 이유를 기록하려 합니다</title>
    <link href="https://blog.archilog.dev/posts/why-archilog/"/>
    <updated>2026-06-08T00:00:00+09:00</updated>
    <id>https://blog.archilog.dev/posts/why-archilog</id>
    <content type="html">&lt;p&gt;프로젝트를 진행하다 보면 많은 결정을 내립니다.&lt;/p&gt;

&lt;p&gt;어떤 기술을 사용할지, 역할의 경계를 어디까지 나눌지, 지금 해결할 문제와 나중으로 미룰 문제를 어떻게 구분할지 고민합니다. 당시에는 분명 많은 생각을 했지만, 프로젝트가 끝나고 나면 결과만 남고 그 과정은 쉽게 잊힙니다.&lt;/p&gt;

&lt;p&gt;아키로그에는 완성된 기술이나 정답보다 그 기술을 선택한 이유를 기록하려 합니다.&lt;/p&gt;

&lt;h2 id=&quot;작은-고민에서-시작된-이야기&quot;&gt;작은 고민에서 시작된 이야기&lt;/h2&gt;

&lt;p&gt;실제 프로젝트의 변화는 거창한 질문보다 작은 불편에서 시작되는 경우가 많았습니다.&lt;/p&gt;

&lt;p&gt;문장 하나를 바꾸는 일, 앱과 웹의 경계를 정하는 일, 디자인과 코드가 같은 기준을 바라보게 하는 일처럼 처음에는 단순해 보였던 문제가 구조에 대한 고민으로 이어졌습니다.&lt;/p&gt;

&lt;h2 id=&quot;결과보다-판단의-과정&quot;&gt;결과보다 판단의 과정&lt;/h2&gt;

&lt;p&gt;기술은 계속 바뀌지만 문제를 바라보고 기준을 세우는 과정은 다음 프로젝트에도 남습니다.&lt;/p&gt;

&lt;p&gt;그래서 아키로그에서는 다음 세 가지를 중심으로 이야기하려 합니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;어떤 상황에서 고민이 시작되었는지&lt;/li&gt;
  &lt;li&gt;선택할 수 있었던 방법과 판단의 기준은 무엇이었는지&lt;/li&gt;
  &lt;li&gt;그 경험을 지나며 생각이 어떻게 달라졌는지&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;꾸준히-남기는-기록&quot;&gt;꾸준히 남기는 기록&lt;/h2&gt;

&lt;p&gt;완벽하게 정리된 답을 설명하기보다, 실제로 일하며 마주한 고민을 담백하게 기록하겠습니다.&lt;/p&gt;

&lt;p&gt;이 기록이 비슷한 문제를 마주한 누군가에게 작은 참고가 되고, 저에게는 다음 선택을 조금 더 나은 방향으로 이끄는 기준이 되었으면 합니다.&lt;/p&gt;

</content>
  </entry>
  
</feed>

