<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>0798_hyuk.log</title>
        <link>https://velog.io/</link>
        <description>스위트아메리카노</description>
        <lastBuildDate>Sat, 04 Apr 2026 08:37:47 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>0798_hyuk.log</title>
            <url>https://velog.velcdn.com/images/0798_hyuk/profile/0fd3d757-0d9a-4a36-9dbb-dcd0f913f47f/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 0798_hyuk.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/0798_hyuk" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[클로드 코드 전코드 유출?]]></title>
            <link>https://velog.io/@0798_hyuk/%ED%81%B4%EB%A1%9C%EB%93%9C-%EC%A0%84%EC%BD%94%EB%93%9C-%EC%9C%A0%EC%B6%9C</link>
            <guid>https://velog.io/@0798_hyuk/%ED%81%B4%EB%A1%9C%EB%93%9C-%EC%A0%84%EC%BD%94%EB%93%9C-%EC%9C%A0%EC%B6%9C</guid>
            <pubDate>Sat, 04 Apr 2026 08:37:47 GMT</pubDate>
            <description><![CDATA[<p>요즘 개발 커뮤니티 보면 다들 한 번씩 얘기하는 이슈가 있죠. 바로 클로드(Claude) 코드 유출 사건.</p>
<p>나도 처음엔 “또 하나의 보안 사고인가 보다” 하고 넘기려다가, 내용 좀 보니까 생각보다 가볍게 넘길 일은 아니더라고요. 그래서 이걸 계기로 느낀 점이랑 정리 좀 해보려고 글 씁니다.</p>
<hr>
<h2 id="사건-배경-간단-정리">사건 배경 (간단 정리)</h2>
<p>최근에 Anthropic의 Claude 관련 내부 코드나 프롬프트 일부가 외부로 유출됐다는 얘기가 돌면서 꽤 이슈가 됐습니다.
정확히 어떤 경로로 유출됐는지는 아직 명확하게 다 공개된 건 아니지만, 핵심은 이거 하나예요.</p>
<blockquote>
<p>“AI 서비스도 결국 사람이 만드는 소프트웨어고, 보안이 뚫리면 똑같이 위험하다”</p>
</blockquote>
<p>사실 GPT든 Claude든 다들 되게 ‘대단한 AI’라고 생각하는데, 그 내부를 보면 결국 코드 + 프롬프트 + 데이터의 조합이거든요.
그게 유출됐다는 건 생각보다 큰 문제입니다.</p>
<hr>
<h2 id="내가-느낀-포인트">내가 느낀 포인트</h2>
<h3 id="1-프롬프트도-자산이다">1. 프롬프트도 ‘자산’이다</h3>
<p>이거 이번에 제일 크게 느낀 부분.</p>
<p>예전에는 그냥 “프롬프트 잘 짜면 좋다~” 정도였는데,
이제는 기업 입장에서는 프롬프트 자체가 거의 핵심 기술이에요.</p>
<ul>
<li>어떻게 답변하게 만드는지</li>
<li>어떤 방식으로 안전하게 필터링하는지</li>
<li>어떤 구조로 추론하게 만드는지</li>
</ul>
<p>이런 게 다 프롬프트에 녹아있거든요.</p>
<p>즉, 코드만 중요한 게 아니라
<strong>“AI를 어떻게 다루는 방식 자체”가 유출된 거</strong>라서 더 의미가 큼.</p>
<hr>
<h3 id="2-결국-보안은-기본-중의-기본">2. 결국 보안은 기본 중의 기본</h3>
<p>이거 보면서 든 생각 하나.</p>
<blockquote>
<p>“아무리 AI 시대라도, 기본적인 보안 못 하면 끝이다”</p>
</blockquote>
<p>요즘은 다들 AI, 모델, 성능 이런 얘기만 하는데
막상 서비스 터지는 건 항상 보안 쪽에서 터짐.</p>
<ul>
<li>API 키 관리</li>
<li>접근 권한 관리 (IAM 같은 거)</li>
<li>내부 코드 관리</li>
</ul>
<p>이거 하나라도 삐끗하면 바로 사고로 이어지는 구조임.</p>
<p>나도 AWS 쓰면서 느끼는 건데,
권한 설정 조금만 잘못해도 바로 위험해지는 게 이쪽임.</p>
<hr>
<h3 id="3-클로즈드-vs-오픈-논쟁-더-커질-듯">3. “클로즈드 vs 오픈” 논쟁 더 커질 듯</h3>
<p>이 사건 이후로 또 나올 얘기 뻔함.</p>
<ul>
<li>“어차피 유출될 거면 오픈소스가 낫다”</li>
<li>“그래도 기업 기술은 보호해야 한다”</li>
</ul>
<p>개인적으로는 둘 다 맞는 말 같음.</p>
<p>근데 확실한 건,
<strong>클로즈드 모델이라고 해서 완전히 안전하지 않다</strong>는 건 이번에 확실히 보여준 사례라고 생각함.</p>
<hr>
<h2 id="개발자로서-느낀-점">개발자로서 느낀 점</h2>
<p>솔직히 이거 보면서 좀 현실적으로 와닿은 게 하나 있음.</p>
<blockquote>
<p>“대단한 기술도 결국 관리 못 하면 무너진다”</p>
</blockquote>
<p>우리는 지금 AI 공부하고, 프로젝트 만들고, 서버 올리고 이런 거 하잖아요.</p>
<p>근데 그 과정에서</p>
<ul>
<li>.env 그냥 공유하고</li>
<li>키 그대로 올리고</li>
<li>권한 대충 주고</li>
</ul>
<p>이러면… 규모만 커졌을 때 똑같은 사고 나는 거임.</p>
<p>결국 중요한 건</p>
<ul>
<li>기술 잘 쓰는 거 +</li>
<li><strong>안전하게 쓰는 거</strong></li>
</ul>
<p>이 두 개를 같이 가져가야 한다는 거.</p>
<hr>
<h2 id="마무리">마무리</h2>
<p>이번 클로드 코드 유출 사건은 단순한 “사고 하나”라기보다,
AI 시대에서 우리가 뭘 중요하게 봐야 하는지 다시 보여준 케이스 같음.</p>
<p>정리하면 딱 이거:</p>
<ul>
<li>프롬프트도 자산이다</li>
<li>보안은 선택이 아니라 필수다</li>
<li>AI도 결국 소프트웨어다</li>
</ul>
<p>나도 이번 기회에
.env 관리, AWS 권한, 키 관리 이런 거 다시 한 번 점검해봐야겠다는 생각 들었음.</p>
<p>괜히 큰 서비스만의 문제가 아니라
우리 같은 개인 프로젝트에서도 충분히 터질 수 있는 문제니까.</p>
<hr>
<p>다음에는 이거 이어서
“개발하면서 실제로 보안 어떻게 챙겨야 하는지”도 정리해볼까 생각 중.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JWT or Session? (프로젝트)]]></title>
            <link>https://velog.io/@0798_hyuk/JWT-or-Session-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</link>
            <guid>https://velog.io/@0798_hyuk/JWT-or-Session-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</guid>
            <pubDate>Tue, 31 Mar 2026 04:22:30 GMT</pubDate>
            <description><![CDATA[<h1 id="요구사항-분석-중-발생한-인증-방식-논쟁과-해결-과정-jwt-vs-session">요구사항 분석 중 발생한 인증 방식 논쟁과 해결 과정 (JWT vs Session)</h1>
<p>프로젝트를 시작하면서 가장 먼저 진행했던 작업은 요구사항 분석이었다.
기능 정의를 구체화하는 과정에서 예상보다 다양한 의견이 나왔는데, 그중에서도 가장 크게 의견이 갈렸던 부분은 <strong>사용자 인증 방식 선택</strong>이었다.</p>
<h2 id="문제-상황">문제 상황</h2>
<p>처음에는 단순히 “로그인 기능을 구현한다” 정도로 생각했지만,
구체적인 구현 방식을 논의하면서 자연스럽게 인증 방식에 대한 이야기가 나왔다.</p>
<p>팀 내에서는 크게 두 가지 의견으로 나뉘었다.</p>
<ul>
<li><strong>JWT 기반 인증을 사용하자는 의견</strong></li>
<li><strong>세션 기반 인증을 사용하자는 의견</strong></li>
</ul>
<p>처음에는 단순한 기술 선택처럼 보였지만, 이야기가 깊어질수록 각 방식의 장단점이 부각되면서 쉽게 결론이 나지 않았다.</p>
<h2 id="논쟁의-핵심">논쟁의 핵심</h2>
<p>JWT를 사용하자는 쪽에서는 다음과 같은 이유를 들었다.</p>
<ul>
<li>서버 상태를 최소화할 수 있어 확장성에 유리하다</li>
<li>MSA 구조를 고려하면 더 적합하다</li>
<li>최근 많이 사용되는 방식이라 학습 가치가 있다</li>
</ul>
<p>반면, 세션 기반을 선호한 쪽에서는 이런 의견이 나왔다.</p>
<ul>
<li>구현이 단순하고 안정적이다</li>
<li>로그아웃이나 인증 관리가 직관적이다</li>
<li>현재 프로젝트 규모에서는 과한 선택일 수 있다</li>
</ul>
<p>결국 이 논쟁의 본질은 단순히 기술의 문제가 아니라
<strong>“개발 속도를 우선할 것인지, 확장성과 구조를 고려할 것인지”</strong>에 대한 방향성 차이였다.</p>
<h2 id="해결-과정">해결 과정</h2>
<p>이 상태로는 아무리 토론을 이어가도 결론이 나기 어렵다고 판단했고,
기술 자체가 아니라 <strong>프로젝트의 목표를 기준으로 의사결정을 해야 한다고 생각했다.</strong></p>
<p>그래서 팀원들과 함께 다음과 같은 기준을 먼저 정리했다.</p>
<ol>
<li>이번 프로젝트의 목적은 단순 기능 구현이 아니라 학습과 경험에 있다</li>
<li>MSA 구조를 일부라도 경험해보는 것이 중요하다</li>
<li>일정 내 구현 가능한 수준이어야 한다</li>
</ol>
<p>이 기준을 바탕으로 다시 논의를 진행한 결과,
<strong>확장성과 학습 측면에서 장점이 있는 JWT 방식이 더 적합하다는 결론</strong>을 내릴 수 있었다.</p>
<p>다만, JWT의 단점으로 언급되었던 토큰 관리 문제는 그대로 두지 않고,
Redis를 활용해 보완하기로 했다.</p>
<h2 id="결과">결과</h2>
<p>결과적으로 JWT 기반 인증을 도입하면서 구조적인 설계 경험을 가져갈 수 있었고,
단순 구현을 넘어 실제 서비스 구조를 고민해볼 수 있는 계기가 되었다.</p>
<p>무엇보다 의미 있었던 점은,
기술 선택을 개인의 선호가 아닌 <strong>명확한 기준을 바탕으로 결정했다는 것</strong>이었다.</p>
<p>이 경험 이후로는 새로운 기능이나 기술을 선택할 때도
자연스럽게 “이 선택이 프로젝트 목표에 맞는가?”를 먼저 고민하게 되었다.</p>
<h2 id="마무리">마무리</h2>
<p>요구사항 분석 단계는 단순히 기능을 나열하는 과정이 아니라,
프로젝트의 방향을 결정하는 중요한 단계라는 것을 느꼈다.</p>
<p>그리고 그 과정에서 발생하는 논쟁은 피해야 할 문제가 아니라,
오히려 더 나은 결정을 위한 필수적인 과정이라고 생각하게 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[토이 프로젝트] 아이폰으로 찍은 수업 사진을 노션에 바로 올리는 서비스 만들기]]></title>
            <link>https://velog.io/@0798_hyuk/%ED%86%A0%EC%9D%B4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%95%84%EC%9D%B4%ED%8F%B0%EC%9C%BC%EB%A1%9C-%EC%B0%8D%EC%9D%80-%EC%88%98%EC%97%85-%EC%82%AC%EC%A7%84%EC%9D%84-%EB%85%B8%EC%85%98%EC%97%90-%EB%B0%94%EB%A1%9C-%EC%98%AC%EB%A6%AC%EB%8A%94-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%A5%BC-%EB%A7%8C%EB%93%A0-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@0798_hyuk/%ED%86%A0%EC%9D%B4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%95%84%EC%9D%B4%ED%8F%B0%EC%9C%BC%EB%A1%9C-%EC%B0%8D%EC%9D%80-%EC%88%98%EC%97%85-%EC%82%AC%EC%A7%84%EC%9D%84-%EB%85%B8%EC%85%98%EC%97%90-%EB%B0%94%EB%A1%9C-%EC%98%AC%EB%A6%AC%EB%8A%94-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%A5%BC-%EB%A7%8C%EB%93%A0-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Mon, 23 Mar 2026 12:02:54 GMT</pubDate>
            <description><![CDATA[<p>수업을 들으면서 필기나 판서, 교수님 화면을 휴대폰으로 찍는 일은 자주 있었다.<br>문제는 사진을 찍는 것보다, 그 사진을 다시 정리하는 과정이 훨씬 번거롭다는 점이었다.</p>
<p>나는 원래 이런 식으로 작업했다.</p>
<ol>
<li>아이폰으로 사진을 찍는다.</li>
<li>카카오톡 <code>나에게 보내기</code>로 사진을 보낸다.</li>
<li>노트북에서 카카오톡을 열어 사진을 저장하거나 복사한다.</li>
<li>다시 노션을 열어서 붙여넣거나 업로드한다.</li>
</ol>
<p>사진 한 장만 옮길 때는 큰 문제가 아닌 것처럼 보이지만, 수업 시간 동안 이 과정을 반복하다 보면 수업을 이해하는 도중 흐름이 자꾸 끊겼다.<br>특히 여러 장을 연속으로 찍는 날에는, 사진 촬영보다 전송과 정리가 더 귀찮게 느껴졌다.</p>
<hr>
<h2 id="문제-상황">문제 상황</h2>
<p>내 환경은 다음과 같았다.</p>
<ul>
<li>휴대폰: 아이폰</li>
<li>노트북: LG 노트북</li>
<li>운영체제: Windows 11</li>
<li>정리 도구: Notion</li>
</ul>
<p>이 조합에서 내가 원했던 것은 단순했다.</p>
<p><code>방금 찍은 사진을 가능한 바로 노트북에서 쓰고 싶다.</code></p>
<p>하지만 실제 사용 경험은 그렇지 않았다.</p>
<h3 id="불편했던-점">불편했던 점</h3>
<ul>
<li>아이폰과 Windows 노트북 사이 사진 전달이 자연스럽게 이어지지 않는다.</li>
<li>매번 카카오톡이나 메신저를 중간 매개로 써야 한다.</li>
<li>사진을 전송한 뒤에도 다시 노션에 업로드하는 단계가 남아 있다.</li>
<li>수업 흐름 중간에 앱을 여러 번 오가야 해서 집중이 깨진다.</li>
<li>사진을 “찍는 행위”보다 “정리해서 옮기는 행위”가 더 피곤해진다.</li>
</ul>
<hr>
<h2 id="문제-정의">문제 정의</h2>
<p>이 프로젝트에서 정의한 핵심 문제는 아래와 같다.</p>
<h3 id="1-촬영과-정리-사이의-단계가-너무-많다">1. 촬영과 정리 사이의 단계가 너무 많다</h3>
<p>사용자는 단지 수업 사진을 기록하고 싶을 뿐인데,<br>실제로는 촬영 → 전송 → 저장 → 업로드라는 여러 단계를 거쳐야 한다.</p>
<p>즉, <strong>정보를 기록하는 행위보다 전달 과정이 더 무거운 문제</strong>가 있었다.</p>
<h3 id="2-아이폰--windows-조합에서는-즉시성이-부족하다">2. 아이폰 + Windows 조합에서는 즉시성이 부족하다</h3>
<p>애플 생태계 안에서는 비교적 자연스럽게 이어질 수 있는 기능들이 있지만,<br>아이폰과 Windows 노트북 조합에서는 사진을 바로 활용하기 위한 연결이 매끄럽지 않다.</p>
<p>즉, <strong>기기 조합 때문에 작업 흐름이 끊기는 문제</strong>가 있었다.
이는 안그래도 허약한 내 수업 집중상태를 끊는데 충분했다.</p>
<p>-&gt; 물론 이미 상용화된 많은서비스들이 있었겠지만 시장조사보다
내가 그냥 한번 만들어보고 싶다는생각이 더 컸던 거 같다.</p>
<h2 id="내가-원한-해결-방향">내가 원한 해결 방향</h2>
<p>이 프로젝트를 만들면서 내가 원한 경험은 명확했다.</p>
<ul>
<li>아이폰으로 사진을 찍는다.</li>
<li>그 사진이 바로 노트북으로 전달된다.</li>
<li>필요하면 노트북 클립보드에서도 바로 쓸 수 있다.</li>
<li>동시에 노션에도 자동으로 정리된다.</li>
<li>수업 시간에만 활성화되도록 해서 실제 사용 맥락에 맞춘다.</li>
</ul>
<p>즉, 내가 원한 것은 단순한 “사진 업로드 기능”이 아니라<br><strong>수업 기록 흐름을 끊지 않는 자동화된 연결 경험</strong>이었다.</p>
<hr>
<h2 id="이-프로젝트의-목표">이 프로젝트의 목표</h2>
<p>이 프로젝트의 목표는 다음과 같다.</p>
<p><strong>아이폰으로 찍은 수업 사진을, Windows 노트북과 Notion으로 최대한 즉시 연결해<br>중간 전달 과정을 줄이고 기록 흐름을 단순화하는 것</strong></p>
<p>정리하면, 이 서비스는 “사진을 잘 올리는 앱”이 아니라<br><strong>수업 중 기록 과정에서 발생하는 불필요한 전송 마찰을 줄이기 위한 도구</strong>로 시작했다.</p>
<hr>
<h2 id="한-줄로-정리하면">한 줄로 정리하면</h2>
<p><code>카카오톡 나에게 보내기 -&gt; 노트북 저장 -&gt; 노션 업로드</code><br>이 반복 작업이 너무 귀찮아서,</p>
<p><strong>아이폰에서 찍은 수업 사진이 노트북과 노션으로 바로 이어지는 흐름을 만들고 싶었다.</strong></p>
<p><img src="https://velog.velcdn.com/images/0798_hyuk/post/f12e356b-5a7d-4a27-88d1-2383f7d4f8d8/image.png" alt=""></p>
<p>위 사진처럼 잘 사진이 인식이 된다
다만 한가지 문제는 학교 와이파이는 보안이 심해서 연동이 안되어 내 핫스팟을 이용을 해야한다. 집 와이파이는 문제없이 구현이 된다.</p>
<p>깃헙 링크 : 
<a href="https://github.com/0798JiHyuk/class-photo-notion">https://github.com/0798JiHyuk/class-photo-notion</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기술 경험]Grafana 처음 써본 후기 (삽질 포함)]]></title>
            <link>https://velog.io/@0798_hyuk/%EA%B8%B0%EC%88%A0-%EA%B2%BD%ED%97%98Grafana-%EC%B2%98%EC%9D%8C-%EC%8D%A8%EB%B3%B8-%ED%9B%84%EA%B8%B0-%EC%82%BD%EC%A7%88-%ED%8F%AC%ED%95%A8</link>
            <guid>https://velog.io/@0798_hyuk/%EA%B8%B0%EC%88%A0-%EA%B2%BD%ED%97%98Grafana-%EC%B2%98%EC%9D%8C-%EC%8D%A8%EB%B3%B8-%ED%9B%84%EA%B8%B0-%EC%82%BD%EC%A7%88-%ED%8F%AC%ED%95%A8</guid>
            <pubDate>Tue, 17 Mar 2026 05:21:32 GMT</pubDate>
            <description><![CDATA[<p>요즘 프로젝트 하면서
“서버 잘 돌아가고 있는지 한눈에 보고 싶다”는 생각이 계속 들었다.</p>
<p>로그 찍어서 보는 건 한계가 있어서
찾아보다가 <strong>Grafana</strong>를 알게 됐다.</p>
<hr>
<h2 id="1-grafana-처음엔-이게-뭔지-몰랐다">1. Grafana? 처음엔 이게 뭔지 몰랐다</h2>
<p>처음엔 그냥
“그래프 그려주는 툴인가?” 정도로 생각했는데</p>
<p>써보니까 느낌이 좀 다르다.</p>
<p><strong>데이터를 예쁘게 보여주는 도구</strong>인데
문제는 “데이터는 알아서 안 가져온다”는 거</p>
<p>이걸 모르고 한참 헤맸다...</p>
<hr>
<h2 id="2-일단-설치부터">2. 일단 설치부터</h2>
<p>복잡할 줄 알았는데 Docker로 바로 띄웠다.</p>
<pre><code class="language-bash">docker run -d -p 3000:3000 --name grafana grafana/grafana</code></pre>
<p>켜고 나서
<a href="http://localhost:3000">http://localhost:3000</a> 들어가니까 바로 화면 나옴</p>
<hr>
<h2 id="3-여기서부터-막혔다">3. 여기서부터 막혔다</h2>
<p>들어가보니까
“그래서 뭘 해야 하지?” 상태됨</p>
<p>아무 그래프도 안 보임
이유는 아무런 데이터가 없었기 떄문이다</p>
<p>Grafana는
혼자서는 아무것도 못하는 애였다...</p>
<hr>
<h2 id="4-prometheus-연결하면서-이해됨">4. Prometheus 연결하면서 이해됨</h2>
<p>찾아보니까 다들
Prometheus랑 같이 쓰길래 연결해봄</p>
<p>이때 깨달음</p>
<p>👉 Prometheus = 데이터 모으는 역할
👉 Grafana = 그걸 보여주는 역할</p>
<p>이거 이해하니까 갑자기 전체 흐름이 보였다</p>
<hr>
<h2 id="5-대시보드-만들기-재밌는-구간">5. 대시보드 만들기 (재밌는 구간)</h2>
<p>데이터 연결하고 나서
Dashboard 만들어봤는데</p>
<p>이때부터 좀 재밌어짐</p>
<p>쿼리 하나 넣었는데
그래프 바로 뜨는 거 보고 약간 감탄함</p>
<pre><code class="language-bash">rate(http_requests_total[1m])</code></pre>
<p>👉 “아 이래서 쓰는구나” 싶었다</p>
<hr>
<h2 id="6-써보면서-느낀-점">6. 써보면서 느낀 점</h2>
<p>좋았던 점</p>
<ul>
<li>생각보다 금방 익숙해짐</li>
<li>시각화가 진짜 직관적임</li>
<li>디버깅할 때 도움 많이 될 듯</li>
</ul>
<p>어려웠던 점</p>
<ul>
<li>처음에 개념 이해가 제일 어려움</li>
<li>데이터 없으면 아무것도 못함 (이거 핵심)</li>
</ul>
<hr>
<h2 id="7-한-줄-정리">7. 한 줄 정리</h2>
<p>👉 Grafana는 혼자 쓰는 게 아니라
👉 다른 도구랑 같이 써야 의미 있음</p>
<hr>
<h2 id="8-다음에-해볼-거">8. 다음에 해볼 거</h2>
<ul>
<li>서버 CPU / 메모리 모니터링 붙이기</li>
<li>API 응답속도 그래프로 보기</li>
<li>알림 설정까지 해보기</li>
</ul>
<hr>
<h2 id="마무리">마무리</h2>
<p>처음엔 좀 막막했는데
구조 이해하고 나니까 왜 쓰는지 알겠다</p>
<p>👉 로그만 보는 거랑은 차원이 다름</p>
<p>다음엔
Prometheus까지 같이 정리해볼 생각
할게 너무 많긴해 <img src="https://velog.velcdn.com/images/0798_hyuk/post/92d31f91-5771-43be-9755-d9b677046b1f/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기술 해결]TypeScript로 만들었던 프로젝트를 Spring Boot로 다시 만들면서 생긴 기술 고민들]]></title>
            <link>https://velog.io/@0798_hyuk/TypeScript%EB%A1%9C-%EB%A7%8C%EB%93%A4%EC%97%88%EB%8D%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-Spring-Boot%EB%A1%9C-%EB%8B%A4%EC%8B%9C-%EB%A7%8C%EB%93%A4%EB%A9%B4%EC%84%9C-%EC%83%9D%EA%B8%B4-%EA%B8%B0%EC%88%A0-%EA%B3%A0%EB%AF%BC%EB%93%A4</link>
            <guid>https://velog.io/@0798_hyuk/TypeScript%EB%A1%9C-%EB%A7%8C%EB%93%A4%EC%97%88%EB%8D%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-Spring-Boot%EB%A1%9C-%EB%8B%A4%EC%8B%9C-%EB%A7%8C%EB%93%A4%EB%A9%B4%EC%84%9C-%EC%83%9D%EA%B8%B4-%EA%B8%B0%EC%88%A0-%EA%B3%A0%EB%AF%BC%EB%93%A4</guid>
            <pubDate>Sun, 08 Mar 2026 23:46:46 GMT</pubDate>
            <description><![CDATA[<p>이전에 TypeScript 기반으로 만들었던 프로젝트를 Spring Boot로 다시 구현하게 되었다.</p>
<p>처음에는 단순히</p>
<blockquote>
<p>&quot;같은 기능을 Java로 다시 구현하면 되겠지&quot;</p>
</blockquote>
<p>라고 생각했다.</p>
<p>하지만 막상 시작해보니 단순한 언어 변경이 아니라 <strong>아키텍처와 개발 방식 자체가 달라지는 작업</strong>이었다.</p>
<p>이번 글에서는 TypeScript 프로젝트를 Spring Boot로 다시 만들면서 생겼던 기술적인 고민과 해결 과정을 정리해보려고 한다.</p>
<hr>
<h1 id="왜-typescript-→-spring-boot로-다시-만들었을까">왜 TypeScript → Spring Boot로 다시 만들었을까</h1>
<p>처음 프로젝트는 다음과 같은 스택으로 만들어졌다.</p>
<pre><code class="language-id=&quot;d1a0k1&quot;">Node.js
TypeScript
Express
MySQL</code></pre>
<p>빠르게 기능을 만들기에는 좋았지만 프로젝트가 커지면서 몇 가지 아쉬움이 생겼다.</p>
<ul>
<li>서비스 구조 관리</li>
<li>인증 처리</li>
<li>코드 구조 유지보수</li>
</ul>
<p>그래서 백엔드 구조를 다시 고민하게 되었고 결국 <strong>Spring Boot 기반으로 다시 구현</strong>해보기로 했다.</p>
<hr>
<h1 id="문제-1-프로젝트-구조를-어떻게-설계할-것인가">문제 1. 프로젝트 구조를 어떻게 설계할 것인가</h1>
<p>TypeScript 프로젝트는 비교적 단순한 구조였다.</p>
<pre><code class="language-id=&quot;d9c2k0&quot;">controllers
services
routes
models</code></pre>
<p>하지만 Spring Boot 프로젝트는 일반적으로 이런 구조를 사용한다.</p>
<pre><code class="language-id=&quot;c1a2c3&quot;">controller
service
repository
entity</code></pre>
<p>여기서 고민이 생겼다.</p>
<blockquote>
<p>TypeScript 프로젝트 구조를 그대로 가져갈까?
아니면 Spring 방식으로 새로 설계할까?</p>
</blockquote>
<p>결론적으로 <strong>Spring의 레이어드 아키텍처를 따르기로 했다.</strong></p>
<pre><code class="language-id=&quot;l2a0d9&quot;">Controller
Service
Repository
Entity</code></pre>
<p>이렇게 구조를 정리하면서 코드의 역할이 더 명확해졌다.</p>
<hr>
<h1 id="문제-2-비동기-처리-방식-차이">문제 2. 비동기 처리 방식 차이</h1>
<p>TypeScript에서는 대부분 비동기 처리를 이렇게 했다.</p>
<pre><code class="language-id=&quot;b3c1a2&quot;">async / await</code></pre>
<p>예를 들어</p>
<pre><code class="language-id=&quot;s8c2x0&quot;">const user = await userRepository.findById(id)</code></pre>
<p>하지만 Spring Boot에서는 기본적으로 <strong>동기 방식</strong>으로 동작한다.</p>
<p>그래서 처음에는 이런 고민이 생겼다.</p>
<blockquote>
<p>Node는 비동기 기반인데
Spring은 왜 동기 방식일까?</p>
</blockquote>
<p>Spring은 <strong>멀티스레드 기반</strong>으로 요청을 처리하기 때문에 대부분의 경우 동기 방식으로도 충분한 성능을 낼 수 있다는 것을 알게 되었다.</p>
<hr>
<h1 id="문제-3-인증-처리-방식">문제 3. 인증 처리 방식</h1>
<p>TypeScript 프로젝트에서는 JWT 인증을 직접 구현했다.</p>
<p>구조는 대략 이렇게 되어 있었다.</p>
<pre><code class="language-id=&quot;f1k2c3&quot;">login
↓
JWT 발급
↓
middleware 인증</code></pre>
<p>하지만 Spring Boot에서는 보통</p>
<pre><code class="language-id=&quot;z3a9k1&quot;">Spring Security</code></pre>
<p>를 사용한다.</p>
<p>여기서 또 고민이 생겼다.</p>
<blockquote>
<p>기존 JWT 로직을 그대로 사용할까?
아니면 Spring Security를 사용할까?</p>
</blockquote>
<p>결국 <strong>Spring Security + JWT 방식</strong>으로 구현했다.</p>
<p>이 과정에서 가장 어려웠던 부분은</p>
<pre><code class="language-id=&quot;t1q2w3&quot;">Security Filter
JWT Filter</code></pre>
<p>구조를 이해하는 것이었다.</p>
<hr>
<h1 id="문제-4-orm-사용-방식-차이">문제 4. ORM 사용 방식 차이</h1>
<p>TypeScript 프로젝트에서는 보통 이런 방식으로 DB를 사용했다.</p>
<pre><code class="language-id=&quot;r4t5y6&quot;">SQL 직접 작성</code></pre>
<p>또는</p>
<pre><code class="language-id=&quot;j7k8l9&quot;">TypeORM</code></pre>
<p>하지만 Spring Boot에서는 보통</p>
<pre><code class="language-id=&quot;m0n1b2&quot;">JPA
Hibernate</code></pre>
<p>를 사용한다.</p>
<p>여기서 또 고민이 생겼다.</p>
<blockquote>
<p>SQL 중심으로 갈까?
ORM을 사용할까?</p>
</blockquote>
<p>결론적으로 <strong>JPA를 사용하되 복잡한 쿼리는 직접 작성</strong>하는 방식으로 정리했다.</p>
<hr>
<h1 id="문제-5-에러-처리-구조">문제 5. 에러 처리 구조</h1>
<p>TypeScript에서는 보통 이렇게 에러 처리를 했다.</p>
<pre><code class="language-id=&quot;u8i9o0&quot;">try / catch</code></pre>
<p>하지만 Spring에서는</p>
<pre><code class="language-id=&quot;p1q2w3&quot;">@ControllerAdvice
@ExceptionHandler</code></pre>
<p>같은 전역 에러 처리를 사용할 수 있었다.</p>
<p>이 구조를 적용하면서 코드가 훨씬 깔끔해졌다.</p>
<hr>
<h1 id="문제-6-환경-설정-관리">문제 6. 환경 설정 관리</h1>
<p>TypeScript 프로젝트에서는 환경변수를 보통 이렇게 관리했다.</p>
<pre><code class="language-id=&quot;a3s4d5&quot;">.env</code></pre>
<p>Spring Boot에서는</p>
<pre><code class="language-id=&quot;f6g7h8&quot;">application.yml
application.properties</code></pre>
<p>를 사용한다.</p>
<p>처음에는 설정이 많아 복잡하게 느껴졌지만 서비스 설정을 한 곳에서 관리할 수 있다는 장점이 있었다.</p>
<hr>
<h1 id="다시-만들면서-느낀-점">다시 만들면서 느낀 점</h1>
<p>같은 기능을 구현하는 프로젝트라도</p>
<pre><code class="language-id=&quot;g8h9j0&quot;">Node.js
Spring Boot</code></pre>
<p>는 개발 방식 자체가 많이 다르다는 것을 느꼈다.</p>
<p>특히 다음 부분에서 차이를 크게 느꼈다.</p>
<ul>
<li>프로젝트 구조</li>
<li>인증 처리 방식</li>
<li>ORM 사용 방식</li>
</ul>
<p>처음에는 단순한 언어 변경이라고 생각했지만 결과적으로는 <strong>백엔드 아키텍처를 다시 고민하는 과정</strong>이 되었다.</p>
<hr>
<h1 id="마무리">마무리</h1>
<p>기존 TypeScript 프로젝트를 Spring Boot로 다시 만들면서 단순히 코드를 옮기는 작업이 아니라 <strong>서비스 구조를 다시 설계하는 경험</strong>을 할 수 있었다.</p>
<p>특히 Spring의 구조적인 설계 방식이 프로젝트가 커질수록 유지보수에 유리하다는 것을 느꼈다.</p>
<p>다음 글에서는</p>
<ul>
<li>Spring Boot + JWT 인증 구현</li>
<li>AWS 서버 배포 과정</li>
</ul>
<p>도 정리해볼 예정이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기술 문제]JWT 로그인 구현하면서 고민했던 것들 (Access Token / Refresh Token 구조)]]></title>
            <link>https://velog.io/@0798_hyuk/JWT-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EB%A9%B4%EC%84%9C-%EA%B3%A0%EB%AF%BC%ED%96%88%EB%8D%98-%EA%B2%83%EB%93%A4-Access-Token-Refresh-Token-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@0798_hyuk/JWT-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EB%A9%B4%EC%84%9C-%EA%B3%A0%EB%AF%BC%ED%96%88%EB%8D%98-%EA%B2%83%EB%93%A4-Access-Token-Refresh-Token-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Sun, 08 Mar 2026 03:39:25 GMT</pubDate>
            <description><![CDATA[<p>팀 프로젝트에서 로그인 기능을 구현하면서 인증 방식을 고민하게 됐다.
처음에는 세션 기반 인증을 사용할까 고민했지만, 결국 <strong>JWT 기반 인증 방식</strong>을 선택했다.</p>
<p>하지만 막상 구현해보니 단순히 로그인 기능을 만드는 것보다 <strong>토큰 관리와 보안 문제</strong>에서 생각보다 많은 고민이 생겼다.</p>
<p>이번 글에서는 JWT 로그인 구조와 Access Token / Refresh Token을 사용하면서 고민했던 점들을 정리해보려고 한다.</p>
<hr>
<h1 id="jwt란-무엇인가">JWT란 무엇인가</h1>
<p>JWT(Json Web Token)는 <strong>사용자 인증 정보를 토큰 형태로 전달하는 방식</strong>이다.</p>
<p>기존 세션 방식과 가장 큰 차이는 이것이다.</p>
<pre><code>세션 인증
서버에 로그인 상태 저장</code></pre><pre><code>JWT 인증
토큰 안에 사용자 정보를 담아서 인증</code></pre><p>즉 서버가 로그인 상태를 저장하지 않아도 된다.</p>
<p>그래서 <strong>Stateless 인증 방식</strong>이라고 부른다.</p>
<hr>
<h1 id="jwt-구조">JWT 구조</h1>
<p>JWT는 크게 3가지로 구성된다.</p>
<pre><code>Header
Payload
Signature</code></pre><p>예시</p>
<pre><code>xxxxx.yyyyy.zzzzz</code></pre><p>각 역할은 다음과 같다.</p>
<h3 id="header">Header</h3>
<p>토큰 타입과 암호화 알고리즘 정보</p>
<h3 id="payload">Payload</h3>
<p>사용자 정보</p>
<p>예시</p>
<pre><code>userId
email
role</code></pre><h3 id="signature">Signature</h3>
<p>토큰 위조 방지를 위한 서명</p>
<hr>
<h1 id="jwt-로그인-흐름">JWT 로그인 흐름</h1>
<p>JWT 로그인 과정은 보통 다음과 같다.</p>
<pre><code>1. 사용자 로그인 요청
2. 서버에서 사용자 인증
3. JWT 토큰 발급
4. 클라이언트가 토큰 저장
5. API 요청 시 토큰 전달</code></pre><p>API 요청 시 보통 이렇게 전달한다.</p>
<pre><code>Authorization: Bearer {JWT}</code></pre><p>서버에서는 토큰을 검증한 후 요청을 처리한다.</p>
<hr>
<h1 id="access-token--refresh-token-구조">Access Token / Refresh Token 구조</h1>
<p>JWT 인증을 구현할 때 가장 많이 사용하는 방식이</p>
<pre><code>Access Token
Refresh Token</code></pre><p>구조이다.</p>
<h3 id="access-token">Access Token</h3>
<p>특징</p>
<ul>
<li>짧은 유효시간</li>
<li>API 요청 시 사용</li>
</ul>
<p>예</p>
<pre><code>15분
30분</code></pre><h3 id="refresh-token">Refresh Token</h3>
<p>특징</p>
<ul>
<li>Access Token 재발급</li>
<li>긴 유효시간</li>
</ul>
<p>예</p>
<pre><code>7일
14일</code></pre><p>로그인 구조는 보통 이렇게 된다.</p>
<pre><code>로그인
↓
Access Token 발급
Refresh Token 발급
↓
Access Token 만료
↓
Refresh Token으로 재발급</code></pre><hr>
<h1 id="jwt를-사용하면서-생긴-고민">JWT를 사용하면서 생긴 고민</h1>
<p>JWT 인증을 구현하면서 몇 가지 고민이 생겼다.</p>
<hr>
<h2 id="1-토큰-저장-위치">1. 토큰 저장 위치</h2>
<p>토큰을 어디에 저장할 것인가?</p>
<p>대표적인 선택지는</p>
<pre><code>LocalStorage
Cookie</code></pre><p>이다.</p>
<p>하지만 LocalStorage는 <strong>XSS 공격 위험</strong>이 있다.</p>
<p>그래서 최근에는</p>
<pre><code>HttpOnly Cookie</code></pre><p>방식을 많이 사용한다.</p>
<hr>
<h2 id="2-로그아웃-처리">2. 로그아웃 처리</h2>
<p>JWT는 Stateless 방식이라 서버에 세션이 없다.</p>
<p>그래서 로그아웃을 해도 토큰이 살아있다.</p>
<p>이 문제를 해결하기 위해 보통</p>
<pre><code>토큰 블랙리스트
Redis 관리</code></pre><p>같은 방법을 사용한다.</p>
<hr>
<h2 id="3-payload에-어디까지-넣어야-할까">3. Payload에 어디까지 넣어야 할까</h2>
<p>JWT Payload에는 사용자 정보를 넣을 수 있다.</p>
<p>예</p>
<pre><code>userId
email
role</code></pre><p>하지만 데이터를 많이 넣으면 토큰이 커진다.</p>
<p>그래서 보통</p>
<pre><code>userId
role</code></pre><p>정도만 넣는 경우가 많다.</p>
<hr>
<h2 id="4-refresh-token-저장-위치">4. Refresh Token 저장 위치</h2>
<p>Refresh Token을 어디에 저장할지도 고민이었다.</p>
<p>대표적인 방법은</p>
<pre><code>DB 저장
Redis 저장</code></pre><p>이다.</p>
<p>팀 프로젝트에서는 관리 편의성을 위해 <strong>DB에 저장하는 방식</strong>을 사용했다.</p>
<hr>
<h1 id="jwt를-사용하면서-느낀-점">JWT를 사용하면서 느낀 점</h1>
<p>JWT는 확실히 <strong>확장성과 분산 시스템에서 장점</strong>이 있는 인증 방식이다.</p>
<p>하지만 단순히 로그인 기능을 구현하는 것만 보면</p>
<pre><code>Session 방식이 더 단순할 수도 있다.</code></pre><p>특히 토큰 관리와 보안 문제까지 고려하면 JWT 구조를 제대로 설계하는 것이 중요하다고 느꼈다.</p>
<hr>
<h1 id="마무리">마무리</h1>
<p>JWT 인증을 구현하면서 느낀 점은 이것이다.</p>
<blockquote>
<p>JWT는 단순한 토큰 인증이 아니라 토큰 관리 구조를 설계하는 문제에 가깝다.</p>
</blockquote>
<p>Access Token / Refresh Token 구조와 토큰 보안 문제까지 고려해야 안정적인 인증 시스템을 만들 수 있다.</p>
<p>다음에는 실제로 <strong>Spring Security에서 JWT 인증을 구현하는 과정</strong>도 정리해볼 예정이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기술 문제]4년 전 Spring 프로젝트를 Spring Boot로 옮기면서 생긴 기술 고민들]]></title>
            <link>https://velog.io/@0798_hyuk/%EA%B8%B0%EC%88%A0-%EB%AC%B8%EC%A0%9C4%EB%85%84-%EC%A0%84-Spring-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-Spring-Boot%EB%A1%9C-%EC%98%AE%EA%B8%B0%EB%A9%B4%EC%84%9C-%EC%83%9D%EA%B8%B4-%EA%B8%B0%EC%88%A0-%EA%B3%A0%EB%AF%BC%EB%93%A4</link>
            <guid>https://velog.io/@0798_hyuk/%EA%B8%B0%EC%88%A0-%EB%AC%B8%EC%A0%9C4%EB%85%84-%EC%A0%84-Spring-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-Spring-Boot%EB%A1%9C-%EC%98%AE%EA%B8%B0%EB%A9%B4%EC%84%9C-%EC%83%9D%EA%B8%B4-%EA%B8%B0%EC%88%A0-%EA%B3%A0%EB%AF%BC%EB%93%A4</guid>
            <pubDate>Fri, 06 Mar 2026 10:59:57 GMT</pubDate>
            <description><![CDATA[<p>최근에 예전에 만들었던 Spring 프로젝트를 다시 열어보게 됐다.</p>
<p>4년 전에 만들었던 프로젝트라 그런지 구조도 꽤 낡아 있었고,
요즘 기준으로 보면 유지보수가 쉽지 않은 구조였다.</p>
<p>그래서 자연스럽게 이런 생각이 들었다.</p>
<blockquote>
<p>&quot;이 프로젝트를 Spring Boot로 옮겨볼까?&quot;</p>
</blockquote>
<p>Spring Boot는 요즘 대부분의 프로젝트에서 사용되고 있고
설정도 훨씬 간단해졌기 때문에 리팩토링 겸 마이그레이션을 해보려고 했다.</p>
<p>하지만 막상 시작해보니 생각보다 고민거리가 많았다.</p>
<hr>
<h1 id="1-xml-설정을-어떻게-처리해야-할까">1. XML 설정을 어떻게 처리해야 할까?</h1>
<p>예전 프로젝트를 열어보자마자 가장 먼저 보였던 건 <strong>엄청난 양의 XML 설정 파일</strong>이었다.</p>
<pre><code>applicationContext.xml
dispatcher-servlet.xml
mybatis-config.xml
security-context.xml</code></pre><p>Spring Boot는 기본적으로 <strong>Java Config 기반</strong>으로 동작하는데
이걸 전부 옮겨야 할까?</p>
<p>처음에는 단순하게 생각했다.</p>
<blockquote>
<p>&quot;XML을 그냥 import 하면 되는 거 아닌가?&quot;</p>
</blockquote>
<p>하지만 문제가 있었다.</p>
<p>Spring Boot는 <strong>Auto Configuration</strong> 기반으로 돌아가는데
기존 XML 설정이 충돌할 가능성이 있었다.</p>
<p>그래서 고민이 생겼다.</p>
<ul>
<li>기존 XML을 유지할까?</li>
<li>아니면 Java Config로 전부 바꿀까?</li>
</ul>
<p>지금 고민 중인 방향은 이렇다.</p>
<p><strong>핵심 설정만 Java Config로 옮기고
나머지는 점진적으로 제거하는 방식</strong></p>
<p>한 번에 다 바꾸면 리스크가 너무 커질 것 같다.</p>
<hr>
<h1 id="2-war-프로젝트를-jar로-바꿔야-할까">2. WAR 프로젝트를 JAR로 바꿔야 할까?</h1>
<p>예전 프로젝트는 <strong>Tomcat에 배포하는 WAR 구조</strong>였다.</p>
<pre><code>mvn package
→ war 파일 생성
→ tomcat/webapps 배포</code></pre><p>하지만 Spring Boot는 보통 이렇게 사용한다.</p>
<pre><code>java -jar application.jar</code></pre><p>여기서 고민이 생겼다.</p>
<blockquote>
<p>WAR 구조를 유지해야 할까
아니면 Boot 스타일 JAR로 바꿔야 할까?</p>
</blockquote>
<p>JAR로 바꾸면 장점이 많다.</p>
<ul>
<li>내장 Tomcat</li>
<li>배포 간단</li>
<li>Docker 사용 쉬움</li>
</ul>
<p>하지만 기존 프로젝트 구조가 WAR 기준으로 만들어져 있어서
생각보다 수정해야 할 부분이 많았다.</p>
<p>특히 다음 부분이 걸렸다.</p>
<pre><code>web.xml
Servlet 설정
Filter 설정
Listener 설정</code></pre><p>이걸 전부 <strong>Spring Boot 방식으로 바꿔야 한다.</strong></p>
<p>그래서 지금은 이 부분을 어떻게 정리할지 고민 중이다.</p>
<hr>
<h1 id="3-로그-시스템을-그대로-써도-될까">3. 로그 시스템을 그대로 써도 될까?</h1>
<p>기존 프로젝트는 로그를 이렇게 사용하고 있었다.</p>
<pre><code>log4j</code></pre><p>하지만 요즘 Spring Boot에서는 대부분</p>
<pre><code>logback</code></pre><p>을 사용한다.</p>
<p>그래서 또 고민이 생겼다.</p>
<blockquote>
<p>기존 log4j를 유지할까?
아니면 Boot 기본 로그 시스템을 사용할까?</p>
</blockquote>
<p>문제는 log4j 설정 파일이 꽤 많이 커스터마이징 되어 있다는 점이다.</p>
<pre><code>log4j.xml</code></pre><p>이걸 그대로 유지하면 빠르게 마이그레이션할 수 있지만
장기적으로 보면 Boot 기본 구조를 따르는 게 맞을 것 같기도 하다.</p>
<hr>
<h1 id="4-mybatis-설정은-그대로-써도-될까">4. MyBatis 설정은 그대로 써도 될까?</h1>
<p>이 프로젝트는 ORM 대신 <strong>MyBatis</strong>를 사용하고 있었다.</p>
<p>구조는 이런 식이었다.</p>
<pre><code>mapper.xml
SqlSessionFactory
SqlSessionTemplate</code></pre><p>Spring Boot에서는 <code>mybatis-spring-boot-starter</code>를 쓰면
설정이 훨씬 단순해진다.</p>
<p>하지만 또 고민이 생긴다.</p>
<p>기존 설정을 보면 이런 코드가 많다.</p>
<pre><code>SqlSessionFactoryBean
MapperScannerConfigurer</code></pre><p>Boot에서는 이런 설정이 대부분 필요 없어진다.</p>
<p>그래서 고민 중이다.</p>
<blockquote>
<p>기존 설정을 유지하면서 옮길까
아니면 Boot 방식으로 새로 구성할까</p>
</blockquote>
<hr>
<h1 id="5-패키지-구조를-바꿔야-할까">5. 패키지 구조를 바꿔야 할까?</h1>
<p>예전 프로젝트의 패키지 구조는 이랬다.</p>
<pre><code>controller
service
dao
vo
util
common</code></pre><p>전형적인 <strong>레이어드 구조</strong>다.</p>
<p>하지만 요즘은 이런 구조도 많이 보인다.</p>
<pre><code>user
order
payment</code></pre><p>즉 <strong>도메인 중심 구조</strong></p>
<p>그래서 또 고민이 생겼다.</p>
<blockquote>
<p>마이그레이션하면서 구조까지 바꿀까?</p>
</blockquote>
<p>하지만 지금 생각은 이렇다.</p>
<p><strong>구조까지 바꾸면 리팩토링 범위가 너무 커진다.</strong></p>
<p>그래서 일단은</p>
<ul>
<li>Boot 마이그레이션 먼저</li>
<li>구조 리팩토링은 나중</li>
</ul>
<p>이 방향으로 가는 게 맞을 것 같다.</p>
<hr>
<h1 id="6-의존성-충돌-문제">6. 의존성 충돌 문제</h1>
<p>마이그레이션을 하다 보니 가장 예상되는 문제는 이것이다.</p>
<pre><code>dependency 충돌</code></pre><p>예전 프로젝트는 이런 라이브러리를 사용하고 있었다.</p>
<pre><code>spring 4.x
jackson 2.x
commons-fileupload</code></pre><p>Spring Boot로 넘어오면
대부분 <strong>Boot가 버전을 관리한다.</strong></p>
<p>그래서 이런 문제가 생길 수 있다.</p>
<pre><code>ClassNotFoundException
NoSuchMethodError</code></pre><p>이 문제는 실제로 꽤 많이 발생한다고 한다.</p>
<p>그래서 dependency를 하나씩 확인하면서
Boot 기준으로 정리할 필요가 있을 것 같다.</p>
<hr>
<h1 id="아직-해결하지-못한-고민">아직 해결하지 못한 고민</h1>
<p>지금 이 마이그레이션을 하면서
여전히 고민 중인 것들이 있다.</p>
<ul>
<li>XML 설정을 어디까지 제거할까?</li>
<li>WAR 구조를 유지할까?</li>
<li>로그 시스템을 바꿀까?</li>
<li>MyBatis 설정을 다시 짤까?</li>
</ul>
<p>생각보다 단순한 작업이 아니었다.</p>
<p>하지만 이런 고민을 하다 보니
예전에 아무 생각 없이 쓰던 Spring 구조를
다시 이해하게 되는 계기가 된 것 같다.</p>
<hr>
<h1 id="앞으로-해보고-싶은-것">앞으로 해보고 싶은 것</h1>
<p>Spring Boot로 옮기면서
다음 것들도 같이 해보고 싶다.</p>
<ul>
<li>Docker 배포</li>
<li>CI/CD 구축</li>
<li>서버 구조 개선</li>
</ul>
<p>단순히 &quot;Spring Boot로 옮겼다&quot;가 아니라
<strong>프로젝트 구조 자체를 한번 정리하는 과정</strong>이 될 것 같다.</p>
<p>아직 마이그레이션은 진행 중이다.</p>
<p>이 과정에서 생기는 문제들을
하나씩 기록해보려고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기술 해결]AWS 프리티어 1GB 서버에서 Spring + MySQL + AI 모델을 돌리려다 서버가 터진 이야기]]></title>
            <link>https://velog.io/@0798_hyuk/%EA%B8%B0%EC%88%A0-%ED%95%B4%EA%B2%B0AWS-%ED%94%84%EB%A6%AC%ED%8B%B0%EC%96%B4-1GB-%EC%84%9C%EB%B2%84%EC%97%90%EC%84%9C-Spring-MySQL-AI-%EB%AA%A8%EB%8D%B8%EC%9D%84-%EB%8F%8C%EB%A6%AC%EB%A0%A4%EB%8B%A4-%EC%84%9C%EB%B2%84%EA%B0%80-%ED%84%B0%EC%A7%84-%EC%9D%B4%EC%95%BC%EA%B8%B0</link>
            <guid>https://velog.io/@0798_hyuk/%EA%B8%B0%EC%88%A0-%ED%95%B4%EA%B2%B0AWS-%ED%94%84%EB%A6%AC%ED%8B%B0%EC%96%B4-1GB-%EC%84%9C%EB%B2%84%EC%97%90%EC%84%9C-Spring-MySQL-AI-%EB%AA%A8%EB%8D%B8%EC%9D%84-%EB%8F%8C%EB%A6%AC%EB%A0%A4%EB%8B%A4-%EC%84%9C%EB%B2%84%EA%B0%80-%ED%84%B0%EC%A7%84-%EC%9D%B4%EC%95%BC%EA%B8%B0</guid>
            <pubDate>Fri, 06 Mar 2026 10:55:24 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 진행하면서 AWS 프리티어 EC2 서버를 사용하고 있었다.
프리티어 인스턴스의 메모리는 <strong>1GB</strong>라서 처음에는 가벼운 서비스 정도는 충분히 돌릴 수 있을 거라고 생각했다.</p>
<p>하지만 프로젝트가 커지면서 문제가 발생했다.</p>
<p>서비스 구조는 대략 이랬다.</p>
<ul>
<li>Backend: Spring Boot</li>
<li>Database: MySQL</li>
<li>AI Model: Python 기반 모델</li>
<li>Infrastructure: AWS EC2 (Free Tier)</li>
</ul>
<p>처음에는 모든 것을 <strong>하나의 서버에서 같이 실행</strong>했다.</p>
<pre><code>Spring Boot
MySQL
AI Model</code></pre><p>결과는 예상보다 빠르게 나타났다.</p>
<p><strong>서버 다운.</strong></p>
<hr>
<h2 id="문제-상황">문제 상황</h2>
<p>처음에는 단순히 서버가 느려지는 수준이었다.</p>
<ul>
<li>API 응답이 느려짐</li>
<li>MySQL 쿼리 지연</li>
<li>AI 모델 inference 시간이 길어짐</li>
</ul>
<p>하지만 어느 순간부터는 아예 서버가 멈췄다.</p>
<p>SSH 접속도 안 되고, EC2 상태를 보면 <strong>메모리 부족(OOM)</strong> 상황이었다.</p>
<p>1GB 메모리 환경에서 동시에 돌아가고 있던 것들:</p>
<ul>
<li>Spring Boot JVM</li>
<li>MySQL</li>
<li>Python AI 모델</li>
</ul>
<p>특히 <strong>Spring Boot의 JVM + Python 모델 메모리 사용량</strong>이 생각보다 컸다.</p>
<p>결국 구조 자체를 다시 고민하게 됐다.</p>
<hr>
<h2 id="고민-서버를-업그레이드할까-아니면-구조를-바꿀까">고민: 서버를 업그레이드할까? 아니면 구조를 바꿀까?</h2>
<p>가장 쉬운 방법은 EC2 인스턴스를 업그레이드하는 것이었다.</p>
<p>하지만 프로젝트 특성상 <strong>프리티어 환경에서 최대한 해결해보고 싶었다.</strong></p>
<p>그래서 두 가지 접근을 했다.</p>
<p>1️⃣ 백엔드와 DB의 <strong>메모리 사용량을 줄인다</strong>
2️⃣ AI 모델을 <strong>서버에서 분리한다</strong></p>
<hr>
<h2 id="1-spring-boot-메모리-튜닝">1. Spring Boot 메모리 튜닝</h2>
<p>Spring Boot는 기본 설정 그대로 실행하면 JVM 메모리를 꽤 많이 사용한다.</p>
<p>그래서 JVM 옵션을 조정했다.</p>
<pre><code>-Xms256m
-Xmx512m</code></pre><p>초기 메모리와 최대 메모리를 제한해서 <strong>Spring이 서버 메모리를 과도하게 점유하지 않도록 설정</strong>했다.</p>
<p>또한 불필요한 dependency를 제거하고
로깅 레벨도 최소화했다.</p>
<hr>
<h2 id="2-mysql-메모리-튜닝">2. MySQL 메모리 튜닝</h2>
<p>MySQL 역시 기본 설정으로 두면 메모리 사용량이 꽤 높다.</p>
<p>그래서 다음 설정을 조정했다.</p>
<p>대표적으로:</p>
<ul>
<li><code>innodb_buffer_pool_size</code></li>
<li><code>max_connections</code></li>
</ul>
<p>프리티어 서버 환경에 맞게 <strong>버퍼 크기를 줄이고 연결 수를 제한</strong>했다.</p>
<p>결과적으로 MySQL 메모리 사용량을 꽤 줄일 수 있었다.</p>
<hr>
<h2 id="3-ai-서버-분리-fastapi">3. AI 서버 분리 (FastAPI)</h2>
<p>가장 큰 문제는 <strong>AI 모델이었다.</strong></p>
<p>Python 기반 모델은 메모리를 꽤 많이 사용하고,
특히 모델이 로드될 때 메모리를 크게 차지했다.</p>
<p>그래서 구조를 다음처럼 변경했다.</p>
<h3 id="기존-구조">기존 구조</h3>
<pre><code>Client
   ↓
Spring Server
   ↓
AI Model (same server)</code></pre><h3 id="변경된-구조">변경된 구조</h3>
<pre><code>Client
   ↓
Spring Server
   ↓
FastAPI AI Server</code></pre><p>AI 모델은 <strong>별도의 서버에서 FastAPI로 운영</strong>했다.</p>
<p>Spring에서는 AI 요청이 필요할 때</p>
<pre><code>POST /predict</code></pre><p>API를 호출하도록 만들었다.</p>
<p>이렇게 하면서 얻은 장점은 명확했다.</p>
<ul>
<li>AI 모델이 <strong>Spring 서버 메모리를 사용하지 않음</strong></li>
<li>AI 서버를 <strong>독립적으로 확장 가능</strong></li>
<li>서비스 구조가 <strong>마이크로서비스 형태로 분리</strong></li>
</ul>
<hr>
<h2 id="결과">결과</h2>
<p>구조를 변경한 이후</p>
<ul>
<li>서버 다운 문제 해결</li>
<li>API 응답 안정화</li>
<li>AI inference 요청도 안정적으로 처리</li>
</ul>
<p>무엇보다 좋았던 점은 <strong>인프라 구조에 대해 더 깊이 고민하게 됐다는 것</strong>이다.</p>
<p>단순히 &quot;서버 사양을 올리는 것&quot;보다</p>
<ul>
<li>메모리 사용 구조</li>
<li>서비스 분리</li>
<li>API 기반 통신</li>
</ul>
<p>같은 것들을 실제로 경험해 볼 수 있었다.</p>
<hr>
<h2 id="배운-점">배운 점</h2>
<p>이번 경험을 통해 느낀 점은 하나였다.</p>
<p><strong>&quot;문제가 생기면 서버를 키우기 전에 구조를 먼저 고민해보자.&quot;</strong></p>
<p>특히 AI 서비스는</p>
<ul>
<li>Backend</li>
<li>Database</li>
<li>AI Inference</li>
</ul>
<p>를 <strong>분리하는 구조가 훨씬 안정적</strong>이라는 걸 직접 체감했다.</p>
<p>AWS 프리티어 같은 제한된 환경에서도
구조를 잘 설계하면 충분히 운영할 수 있다는 것도 배웠다.</p>
<hr>
<h2 id="다음-목표">다음 목표</h2>
<p>다음에는 다음 부분도 개선해보고 싶다.</p>
<ul>
<li>Docker 기반 서비스 분리</li>
<li>Nginx Reverse Proxy 적용</li>
<li>AI 서버 GPU 환경 실험</li>
</ul>
<p>아직 작은 프로젝트지만
이런 기술 고민들이 쌓여서 더 좋은 구조를 만들 수 있다고 생각한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[개발자 일기]🏆 GDGoC 연합 해커톤 후기 – 취업난 속에서 우리가 만든 것]]></title>
            <link>https://velog.io/@0798_hyuk/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%9D%BC%EA%B8%B0-GDGoC-%EC%97%B0%ED%95%A9-%ED%95%B4%EC%BB%A4%ED%86%A4-%ED%9B%84%EA%B8%B0-%EC%B7%A8%EC%97%85%EB%82%9C-%EC%86%8D%EC%97%90%EC%84%9C-%EC%9A%B0%EB%A6%AC%EA%B0%80-%EB%A7%8C%EB%93%A0-%EA%B2%83</link>
            <guid>https://velog.io/@0798_hyuk/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%9D%BC%EA%B8%B0-GDGoC-%EC%97%B0%ED%95%A9-%ED%95%B4%EC%BB%A4%ED%86%A4-%ED%9B%84%EA%B8%B0-%EC%B7%A8%EC%97%85%EB%82%9C-%EC%86%8D%EC%97%90%EC%84%9C-%EC%9A%B0%EB%A6%AC%EA%B0%80-%EB%A7%8C%EB%93%A0-%EA%B2%83</guid>
            <pubDate>Sun, 15 Feb 2026 09:53:02 GMT</pubDate>
            <description><![CDATA[<h4 id="🏆-gdgoc-연합-해커톤-후기--취업난-속에서-우리가-만든-것">🏆 GDGoC 연합 해커톤 후기 – 취업난 속에서 우리가 만든 것</h4>
<p><a href="https://velog.velcdn.com/images/0798_hyuk/post/e96e74ac-5793-4e4e-9947-b79820a68012/image.jpg"></a></p>
<p>처음 해커톤에 나갔다.
결과는 장려상.</p>
<p>상보다 더 의미 있었던 건
“지금 시대에 개발자는 어떤 사람이 되어야 하는가”에 대한 생각이었다.</p>
<p><strong>📌 1. 주제 선정</strong></p>
<p>GDGoC 연합동아리에서 주최한 해커톤의 주제는 취업난이었다.</p>
<p>취준생인 나에게 이 주제는 멀지 않았다.</p>
<p>정보는 넘쳐난다.</p>
<p>하지만 정리할 시간은 없다.</p>
<p>뉴스는 보지만 면접 답변으로 연결하지 못한다.</p>
<p>우리는 여기서 문제를 정의했다.</p>
<p>“취준생은 정보가 부족한 게 아니라,
정보를 ‘면접에서 쓸 수 있는 형태’로 가공할 시간이 부족하다.”</p>
<p><strong>💡 우리가 만든 서비스 – 스포일러 (Spoiler)</strong>
한 줄 설명</p>
<p>관심 기업과 직무를 설정하면
AI가 면접 관점으로 요약해 푸시 알림으로 전달하는 초개인화 면접 비서</p>
<p><strong>🔎 문제 정의</strong></p>
<p>기업 분석은 중요하지만 지속하기 어렵다.</p>
<p>뉴스는 많지만 “면접 답변용”으로 정리되지 않는다.</p>
<p>앱을 매번 열어 확인하는 행동 자체가 피로하다.</p>
<p><strong>🚀 해결 전략</strong></p>
<p>우리는 역설적인 UX를 택했다.</p>
<p>“사용자가 앱을 켜지 않게 만드는 앱”</p>
<p>잠금화면 푸시 알림에서 1줄 핵심 요약</p>
<p>탭하면 3줄 요약 + 면접 활용 포인트</p>
<p>직무별 예상 질문 자동 생성</p>
<p><strong>🧠 기술 설계</strong></p>
<p>나는 팀에서 백엔드를 맡았다.</p>
<p>🔹 왜 Node.js를 선택했는가?</p>
<p>해커톤은 속도가 생명이라도 들었다
빠른 개발, JS 생태계 활용, 비동기 처리를 통한 뉴스 수집, 데이터 가공을 위해 선택했다.</p>
<p>🔹 왜 Express 기반 구조를 사용했는가?</p>
<p>빠른 REST API 구성</p>
<p>미들웨어 구조로 확장 가능</p>
<p>데모에 최적화된 가벼운 구조</p>
<p>해커톤에서는 “완벽한 아키텍처”보다
빠르고 안정적인 MVP 구현이 더 중요했다.</p>
<p>사실 익숙한 언어는 java 프레임워크는 Spring Boot 이였지만 처음이라 멋도 모르고 Node.js 로시작하였다..
그러고 후회는 엄청했다.</p>
<p>🔹 AI 파이프라인 설계</p>
<p>우리는 단순 요약이 아니라
“면접형 재해석”을 목표로 했다.</p>
<p>처리 흐름</p>
<ol>
<li><p>뉴스 수집 (API 기반)</p>
</li>
<li><p>필터링 (중복/광고 제거)</p>
</li>
<li><p>LLM 호출</p>
</li>
<li><p>직무별 질문 생성</p>
</li>
<li><p>DB 저장</p>
</li>
<li><p>푸시 전송</p>
</li>
</ol>
<p>여기서 핵심은 프롬프트 설계였다.</p>
<p>“이 뉴스를 면접관 관점에서 재해석하라.”</p>
<p>단순 요약이 아니라
질문으로 변환시키는 로직이 우리 서비스의 차별점이었다.</p>
<h3 id="회고록">[회고록]</h3>
<p>🔥 인상 깊었던 순간</p>
<p>해커톤 리더 분이 이런 말을 했다.</p>
<p>“이제는 코더가 아니라 리뷰어가 살아남는다.”</p>
<p>그 말이 머리에 계속 맴돌았다.</p>
<p>AI가 코드를 작성하는 시대에
개발자는 점점 코드 작성자에서
의사결정자, 검증자, 구조 설계자로 이동하고 있다.</p>
<p>이미 이전부터 느꼈지만 이번 해커톤에서 더 느낀거 같다.</p>
<p>AI가 코드 초안을 만들고</p>
<p>우리는 그걸 다듬고</p>
<p>구조를 설계하고</p>
<p>트레이드오프를 판단했다</p>
<p>이게 바로 “리뷰어로서의 개발”이 아닐까 생각했다.</p>
<p>🤝 팀 구성과 협업</p>
<p>총 4명 팀</p>
<p>나는 백엔드</p>
<p>해커톤은 첫 경험</p>
<p>처음이라 긴장도 많이 했다.</p>
<p>하지만:</p>
<p>역할 분배</p>
<p>API 스펙 정리</p>
<p>빠른 피드백 루프</p>
<p>이 과정을 겪으며
“개발은 혼자가 아니라 팀 스포츠”라는 걸 실감했다.</p>
<p>사실 고난도 있었다.
처음 해커톤이라 아키텍처 설계부터 시간분배 등등 빵꾸가 너무나도 많았다
솔직히 처음 기획한 api를 다 만들지도 못했다.
그래도 문제정의에서 해결하고자 한 기능들은 구현을 했고 핵심 로직또한 구현은 완료하였다.
그저 아쉬울뿐..</p>
<p>이번 해커톤을 통해 위처럼 힘든점도 있었지만 얻은것 또한 많았다</p>
<p>짧은시간안에 AI 파이프라인 설계하는 경험을 하였고
개인화 데이터 설계
직무 기반 동적 콘텐츠 설계등을 해볼 수 있었다.</p>
<p>두번쨰로는</p>
<p>나는 그동안 “기능 구현” 중심으로 생각했다.
이번에는 달랐다.
왜 이 기능이 필요한가?
사용자 행동은 어떻게 바뀌는가?
이 서비스가 실제 취준생에게 의미 있는가?</p>
<p>문제 정의 → 해결 설계 → 구현</p>
<p>이 흐름을 처음으로 제대로 경험했다.</p>
<h4 id="🏆-그리고-결과">🏆 그리고 결과</h4>
<p>장려상 수상.</p>
<p>물론 대상은 아니었지만,
처음 해커톤에서 이 정도 결과는 충분히 의미 있었다고 생각한다.</p>
<p>하지만 상보다 더 값진 건</p>
<p>“나는 단순 코더가 아니라
문제를 정의하려는 개발자가 되고 있다”</p>
<p>는 확신이었다.</p>
<p>🎯 앞으로의 방향</p>
<p>이번 해커톤을 통해 느꼈다.</p>
<p>AI 시대에 살아남는 개발자는:</p>
<p>코드를 많이 치는 사람이 아니라</p>
<p>구조를 설계하는 사람</p>
<p>문제를 정의하는 사람</p>
<p>AI를 도구로 활용하는 사람</p>
<p>나는 이제
코더가 아니라 리뷰어이자 설계자가 되기 위한 준비를 시작하려 한다.</p>
<p>마무리</p>
<p>취업난 속에서
우리는 또 하나의 취업 준비 서비스를 만들었다.</p>
<p>아이러니하지만,
그 과정이 나를 더 단단하게 만들었다.</p>
<p>다음 해커톤에서는
더 깊은 설계와 더 강한 문제 정의로 도전해보고 싶다.</p>
<p>notion : 
<a href="https://www.notion.so/GDGoC-2ff3762acb5c80f99e2ff0ff6ca51043">https://www.notion.so/GDGoC-2ff3762acb5c80f99e2ff0ff6ca51043</a></p>
<p>github : 
<a href="https://github.com/nalleum/nalleum_backend">https://github.com/nalleum/nalleum_backend</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로젝트 기록] 보이스피싱 대응 훈련 백엔드(청음) 개선기]]></title>
            <link>https://velog.io/@0798_hyuk/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B8%B0%EB%A1%9D-%EB%B3%B4%EC%9D%B4%EC%8A%A4%ED%94%BC%EC%8B%B1-%EB%8C%80%EC%9D%91-%ED%9B%88%EB%A0%A8-%EB%B0%B1%EC%97%94%EB%93%9C%EC%B2%AD%EC%9D%8C-%EA%B0%9C%EC%84%A0%EA%B8%B0</link>
            <guid>https://velog.io/@0798_hyuk/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B8%B0%EB%A1%9D-%EB%B3%B4%EC%9D%B4%EC%8A%A4%ED%94%BC%EC%8B%B1-%EB%8C%80%EC%9D%91-%ED%9B%88%EB%A0%A8-%EB%B0%B1%EC%97%94%EB%93%9C%EC%B2%AD%EC%9D%8C-%EA%B0%9C%EC%84%A0%EA%B8%B0</guid>
            <pubDate>Sun, 15 Feb 2026 09:16:59 GMT</pubDate>
            <description><![CDATA[<h1 id="프로젝트-기록-보이스피싱-대응-훈련-백엔드guardian-server-개선기">[프로젝트 기록] 보이스피싱 대응 훈련 백엔드(guardian-server) 개선기</h1>
<p>데이콘(공모전 주최) 링크 : <a href="https://dacon.io/competitions/official/236666/overview/description">https://dacon.io/competitions/official/236666/overview/description</a>
공용문서 링크 : 
<a href="https://www.notion.so/2fd189dee3ba80fb8e4ed264bd944ea2">https://www.notion.so/2fd189dee3ba80fb8e4ed264bd944ea2</a>
깃헙 링크 : 
<a href="https://github.com/0798JiHyuk/-repository">https://github.com/0798JiHyuk/-repository</a>
(추후 조직링크로 변경..)</p>
<h2 id="한-줄-소개">한 줄 소개</h2>
<p>실전형 보이스피싱 대응 학습을 위해, 숏폼/롱폼 훈련 결과를 자동 집계하고 주간 리포트까지 생성하는 백엔드 기능을 고도화했다.</p>
<h2 id="프로젝트-배경">프로젝트 배경</h2>
<p>이번 작업의 목표는 크게 두 가지였다.</p>
<ol>
<li>사용자 훈련 데이터를 끝나자마자 요약해 리포트로 제공하기</li>
<li>롱폼 시뮬레이터 답변 품질을 높이기 위해 대화 맥락(history)을 안정적으로 전달하기</li>
</ol>
<p>기존에는 <code>weekly_reports</code> 조회 API는 있었지만, 실제 리포트 생성 로직이 비어 있어 체감 가능한 피드백 경험이 약했다. 또한 롱폼 응답 생성 시 직전 맥락 반영이 제한적이라 답변 일관성이 흔들리는 문제가 있었다.</p>
<h2 id="이번에-구현한-핵심-작업">이번에 구현한 핵심 작업</h2>
<h3 id="1-주간-리포트-생성-서비스-구현">1) 주간 리포트 생성 서비스 구현</h3>
<p>파일: <code>src/services/reports.service.ts</code></p>
<p>기존 <code>export const reportsService = {};</code> 상태에서 실제 생성 로직을 완성했다.</p>
<ul>
<li>최근 7일 기준 기간 계산 (<code>from_date</code>, <code>to_date</code>)</li>
<li><code>training_results</code> 기준 훈련 횟수/평균 점수/정확도 집계</li>
<li>모드별 취약 유형 추출<ul>
<li><code>short</code>: <code>training_shorts.category_code</code></li>
<li><code>long</code>: <code>training_scenarios.category</code></li>
</ul>
</li>
<li>OpenAI Responses API를 호출해 한국어 요약 생성<ul>
<li><code>one_line_feedback</code></li>
<li><code>weak_patterns[]</code></li>
<li><code>action_guides[]</code></li>
</ul>
</li>
<li><code>weekly_reports</code> upsert 처리 (<code>ON CONFLICT</code>)</li>
<li>FK 이슈(에러코드 <code>23503</code>) 발생 시 <code>top_wrong_type_code=null</code>로 재시도하는 안전장치 추가</li>
</ul>
<p>핵심 포인트는 &quot;집계 SQL + AI 요약 + DB upsert&quot;를 한 사이클로 묶어, 조회 전에 데이터가 만들어지는 구조를 만든 것이다.</p>
<h3 id="2-훈련-종료-시-자동-리포트-생성-연결">2) 훈련 종료 시 자동 리포트 생성 연결</h3>
<p>파일:</p>
<ul>
<li><code>src/routes/trainingShort.routes.ts</code></li>
<li><code>src/routes/trainingLong.routes.ts</code></li>
</ul>
<p>숏폼/롱폼 세션 <code>finish</code> 시점에 <code>generateWeeklyReport(userId, mode)</code>를 호출하도록 연결했다.</p>
<ul>
<li>학습 직후 리포트 최신화</li>
<li>리포트 생성 실패가 학습 완료 응답 자체를 막지 않도록 <code>try/catch + warn</code> 처리</li>
</ul>
<p>즉, 사용자 경험 관점에서 &quot;훈련 완료 -&gt; 바로 리포트 확인&quot; 흐름이 자연스럽게 이어지게 했다.</p>
<h3 id="3-리포트-조회-api-안정성-개선">3) 리포트 조회 API 안정성 개선</h3>
<p>파일: <code>src/routes/reports.routes.ts</code></p>
<p><code>/api/reports/weekly</code>에서 다음을 개선했다.</p>
<ul>
<li>정렬 기준을 <code>generated_at DESC</code>로 변경해 가장 최근 생성본을 확실히 반환</li>
<li>요청/결과 로그 추가 (<code>[reports/weekly] request/result</code>)</li>
<li>예외 발생 시 503 + 구조화된 에러 코드(<code>REPORT_QUERY_FAILED</code>) 반환</li>
</ul>
<p>운영 중 장애 분석을 빠르게 하려는 목적이 컸다.</p>
<h3 id="4-롱폼-대화-맥락-전달history-개선">4) 롱폼 대화 맥락 전달(History) 개선</h3>
<p>파일:</p>
<ul>
<li><code>src/routes/trainingLong.routes.ts</code></li>
<li><code>src/services/ai.service.ts</code></li>
<li><code>src/services/simulator.service.ts</code></li>
<li><code>scripts/simulator_bridge.py</code></li>
</ul>
<p>변경 내용:</p>
<ul>
<li>서버가 <code>longform_messages</code>에서 기존 대화를 조회해 <code>history</code> 배열로 구성</li>
<li><code>aiGenerateLongformReply</code> 및 시뮬레이터 클라이언트 계층에 <code>history</code> 파라미터 추가</li>
<li>Python 브리지에서 받은 <code>history</code>를 시뮬레이터 메시지 컨텍스트로 재구성</li>
</ul>
<p>효과:</p>
<ul>
<li>턴이 누적되어도 답변 문맥 일관성 향상</li>
<li>역할(role) 매핑 정리(<code>ai -&gt; assistant</code>)로 모델 입력 안정화</li>
</ul>
<h3 id="5-텍스트-인코딩음성-생성-품질-보강">5) 텍스트 인코딩/음성 생성 품질 보강</h3>
<p>파일:</p>
<ul>
<li><code>feedback_agent.py</code></li>
<li><code>simulator_test_2.py</code></li>
</ul>
<p>주요 개선:</p>
<ul>
<li>UTF-8 안전 정제 함수 추가로 깨진 문자열/서로게이트 문자 대응</li>
<li>TTS 입력 텍스트 전처리 강화(기호 정리, 문장부호 정돈)</li>
<li>프롬프트 메시지를 JSON 형태로 stderr 출력해 디버깅 가시성 확보</li>
</ul>
<h2 id="트러블슈팅에서-배운-점">트러블슈팅에서 배운 점</h2>
<ol>
<li><p>리포트 기능은 &quot;조회 API&quot;만 만들면 끝이 아니었다.
실제 생성 트리거를 사용자 행동 지점(<code>finish</code>)에 붙여야 데이터가 살아 움직인다.</p>
</li>
<li><p>AI 요약은 항상 실패 가능성을 전제로 설계해야 한다.
API 키 누락, 파싱 실패, DB 제약 이슈를 각각 분리해 fallback/재시도를 넣어야 운영이 안정된다.</p>
</li>
<li><p>대화형 시스템에서 문맥(history)은 품질의 핵심이다.
한 턴의 입력만 보내는 구조보다, 정돈된 대화 이력을 전달할 때 응답 품질이 확실히 좋아진다.</p>
</li>
</ol>
<h2 id="현재-아키텍처-흐름요약">현재 아키텍처 흐름(요약)</h2>
<ol>
<li>사용자가 숏폼/롱폼 훈련 진행</li>
<li>세션 종료 API 호출</li>
<li>서버가 최근 7일 학습 데이터를 집계</li>
<li>AI가 취약 패턴/행동 가이드를 요약</li>
<li><code>weekly_reports</code> upsert</li>
<li><code>/api/reports/weekly</code>에서 최신 리포트 조회</li>
</ol>
<h2 id="기술-스택과-선택-이유">기술 스택과 선택 이유</h2>
<h3 id="사용-언어">사용 언어</h3>
<ul>
<li>TypeScript (Node.js): API 서버의 핵심 로직(라우팅, 서비스, DB 연동)을 타입 안정성 있게 운영하기 위해 선택했다. DTO/응답 스키마가 많은 백엔드에서 런타임 오류를 줄이고 리팩터링 안정성을 높이는 데 유리했다.</li>
<li>Python: 시뮬레이터 브리지와 피드백/음성 관련 실험 코드에 사용했다. AI/오디오 처리 라이브러리 생태계가 강하고, 프로토타이핑 속도가 빨라 모델 연동 실험에 적합했다.</li>
</ul>
<h3 id="사용-프레임워크라이브러리">사용 프레임워크/라이브러리</h3>
<ul>
<li>Express: REST API를 빠르게 구성하고 미들웨어 기반으로 인증, 에러 핸들링, 라우트 분리를 명확히 하기 쉬워서 사용했다.</li>
<li>PostgreSQL(<code>pg</code>): 훈련 결과 집계, 주간 리포트 upsert, 조인 기반 통계 쿼리가 핵심이라 관계형 DB가 적합했다.</li>
<li>Zod: 요청 유효성 검증을 스키마로 명시해 입력 신뢰도를 높이고, 잘못된 요청을 초기에 차단하기 위해 사용했다.</li>
<li><code>express-session</code>: 로그인 기반 사용자 식별(<code>req.session.user.id</code>)이 필요해 세션 인증 구조를 적용했다.</li>
<li>AWS S3 SDK(<code>@aws-sdk/client-s3</code>, <code>@aws-sdk/s3-request-presigner</code>): 음성 파일 업로드/조회 URL을 안전하게 관리하기 위해 presigned URL 방식을 사용했다.</li>
<li>OpenAI API(Responses/Chat): 주간 리포트 요약 생성, 대화형 시뮬레이터 응답, 피드백 분석 등 생성형 AI 기능을 백엔드 서비스 계층에 통합했다.</li>
</ul>
<h3 id="백엔드-관점에서의-선택-포인트">백엔드 관점에서의 선택 포인트</h3>
<ol>
<li>유지보수성: TypeScript + 계층형 구조(routes/services/repositories)로 기능 확장 시 영향 범위를 예측하기 쉬웠다.</li>
<li>운영 안정성: 리포트 생성 실패를 업무 실패로 전파하지 않고 fallback/재시도를 넣어 사용자 플로우를 보호했다.</li>
<li>데이터 신뢰성: PostgreSQL 집계 + <code>ON CONFLICT</code> upsert로 주간 리포트 생성의 중복/정합성 문제를 줄였다.</li>
<li>확장성: Express 라우트 분리와 서비스 모듈화 덕분에 숏폼/롱폼/리포트/업로드 기능을 독립적으로 진화시키기 좋았다.</li>
<li>실험 속도: AI 실험 영역은 Python으로, 운영 API는 TypeScript로 분리해 개발 속도와 안정성의 균형을 맞췄다.</li>
</ol>
<h2 id="마무리">마무리</h2>
<p>이번 작업으로 guardian-server는 단순 훈련 제공 백엔드에서, &quot;학습 결과를 자동으로 요약하고 다음 행동까지 제시하는 피드백 백엔드&quot;로 한 단계 올라갔다.</p>
<p>다음 단계로는 리포트 정확도 개선(카테고리 정규화), 장기 추세 리포트(4주/8주), 사용자별 개인화 코칭 템플릿을 확장해볼 계획이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[개발자 일기]AI 발전으로 인한 내 일자리 지키기 ]]></title>
            <link>https://velog.io/@0798_hyuk/AI-%EB%B0%9C%EC%A0%84%EC%9C%BC%EB%A1%9C-%EC%9D%B8%ED%95%9C-%EB%82%B4-%EC%9D%BC%EC%9E%90%EB%A6%AC-%EC%A7%80%ED%82%A4%EA%B8%B0</link>
            <guid>https://velog.io/@0798_hyuk/AI-%EB%B0%9C%EC%A0%84%EC%9C%BC%EB%A1%9C-%EC%9D%B8%ED%95%9C-%EB%82%B4-%EC%9D%BC%EC%9E%90%EB%A6%AC-%EC%A7%80%ED%82%A4%EA%B8%B0</guid>
            <pubDate>Sun, 15 Feb 2026 08:54:13 GMT</pubDate>
            <description><![CDATA[<h4 id="-컴퓨터공학과-4학년이-마주한-현실">– 컴퓨터공학과 4학년이 마주한 현실</h4>
<p>2026년, 컴퓨터공학과 4학년이 되었다.</p>
<p>예전에는 “컴공이면 취업은 걱정 없다”는 말이 있었다.
하지만 지금은 다르다.</p>
<p>ChatGPT, Copilot, Claude 같은 AI가
코드를 대신 짜고, 리팩토링하고, 심지어 설계까지 제안한다.</p>
<p>나는 지금 이런 질문을 스스로에게 던지고 있다.</p>
<p>“내가 4년 동안 배운 것들이 AI보다 가치 있을까?”</p>
<h4 id="📌-현재-나의-스펙-정리">📌 현재 나의 스펙 정리</h4>
<p>FE 국비 수업 6개월 (NCS 과정)</p>
<p>BE·AI 개발자 취업 준비 국비 7개월</p>
<p>공모전 장려상</p>
<p>해커톤 장려상</p>
<p>포토샵 자격증</p>
<p>교내 프로제트 장려상</p>
<p>외 여러 토이 프로젝트 경험 등</p>
<p>나쁘지 않다.
하지만 솔직히 말하면 차별화는 부족하다.</p>
<p>요즘 신입 지원자들은:</p>
<p>토이 프로젝트 다수</p>
<p>깃허브 잔디 빽빽</p>
<p>블로그 운영</p>
<p>AI 활용 경험</p>
<p>단순히 “웹 서비스 만들어봤습니다”는
더 이상 경쟁력이 되지 않는 시대다.</p>
<h4 id="🔎-문제-정의">🔎 문제 정의</h4>
<p><strong>1️⃣ 단순 코더의 가치 하락</strong></p>
<p>CRUD 구현
로그인 기능
게시판 만들기</p>
<p>이제는 AI가 몇 분이면 만든다.</p>
<p>내가 “기능 구현자”로 남는다면
나는 대체 가능한 인력이 된다.</p>
<p><strong>2️⃣ 설계 경험의 부재</strong></p>
<p>학교에서는 이론을 배웠다.
국비에서는 기능을 만들었다.</p>
<p>하지만 나는 아직:</p>
<p>대규모 트래픽을 설계해본 적 없다.</p>
<p>장애 대응 경험이 없다.</p>
<p>실제 서비스 운영 경험이 없다.</p>
<p>즉, “문제 해결자”라기보다는
“문제 수행자”에 가깝다.</p>
<p><strong>3️⃣ AI를 경쟁자로 볼 것인가, 도구로 볼 것인가</strong></p>
<p>AI는 이미 개발자의 일부 업무를 대체하고 있다.</p>
<p>하지만 동시에
개발자의 생산성을 폭발적으로 올려주고 있다.</p>
<p>결국 질문은 이것이다.</p>
<p>AI를 쓰는 개발자가 될 것인가,
AI에게 밀리는 개발자가 될 것인가?</p>
<h4 id="🎯-앞으로-해결해야-할-것들">🎯 앞으로 해결해야 할 것들</h4>
<p><strong>1️⃣ 기능 구현자 → 문제 정의자</strong></p>
<p>코드를 짜는 사람이 아니라</p>
<p>왜 이런 구조가 필요한지 설명할 수 있는 사람</p>
<p>트레이드오프를 말할 수 있는 사람</p>
<p>기술 선택의 근거를 제시할 수 있는 사람</p>
<p>이 되어야 한다.</p>
<p><strong>2️⃣ CS 기초를 다시 잡기</strong></p>
<p>운영체제</p>
<p>네트워크</p>
<p>데이터베이스 인덱스</p>
<p>트랜잭션</p>
<p>동시성</p>
<p>이걸 모르면 설계를 할 수 없다.</p>
<p>“프레임워크 사용자”에서
“시스템 이해자”로 가야 한다.</p>
<p><strong>3️⃣ AI 활용 능력 키우기</strong></p>
<p>AI를 금지하는 게 아니라
AI를 지휘하는 개발자가 되어야 한다.
요즘은 AI오케스트레이션이라 한다고한다..</p>
<p>코드 리뷰에 AI 활용</p>
<p>리팩토링 보조</p>
<p>테스트 코드 생성</p>
<p>아키텍처 초안 설계</p>
<p>AI는 내 대체자가 아니라
내 생산성을 3배 올려주는 도구가 되어야 한다.</p>
<p><strong>🚀 단기 목표 (1년 이내)</strong></p>
<p>기술 블로그 주 1회 작성</p>
<p>단순 기능 프로젝트가 아닌 “구조 중심 프로젝트” 진행</p>
<p>백엔드 심화 학습 (DB 설계, 캐싱, 동시성)</p>
<p>코드 리뷰 습관화</p>
<p>기술 면접 대비 CS 정리</p>
<p><strong>📈 중기 목표 (3~5년)</strong></p>
<p>서비스 운영 경험 쌓기</p>
<p>트래픽 처리 경험</p>
<p>설계 참여</p>
<p>아키텍처 이해</p>
<p>기술 의사결정 경험</p>
<p>“시킨 것만 하는 개발자”가 아니라
“방향을 제시하는 개발자”가 되는 것.</p>
<p><strong>🧠 결론</strong></p>
<p>AI는 개발자를 없애지 않는다. <del>그러길 빈다.</del></p>
<p>하지만 평범한 개발자는 빠르게 줄어든다.</p>
<p>나는 지금 갈림길에 서 있다.</p>
<p>튜토리얼 개발자로 남을 것인가</p>
<p>문제 해결형 개발자로 성장할 것인가</p>
<p>이 블로그는
내 일자리를 지키기 위한 기록이다.</p>
<p>그리고
앞으로 성장 과정을 증명하는 공간이 될 것이다.</p>
]]></description>
        </item>
    </channel>
</rss>