<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Good Day.</title>
        <link>https://velog.io/</link>
        <description>미림마이스터고등학교 SW과 3학년</description>
        <lastBuildDate>Thu, 07 May 2026 14:47:40 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Good Day.</title>
            <url>https://velog.velcdn.com/images/hjun-kr/profile/3d0c92b0-3fd5-4e1a-acb0-62dfd19dbc3e/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Good Day.. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hjun-kr" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[개발자 취준생은 무엇을 공부해야 하는가? (Ep. 1)]]></title>
            <link>https://velog.io/@hjun-kr/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%B7%A8%EC%A4%80%EC%83%9D%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%84-%EA%B3%B5%EB%B6%80%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80-Ep.-1</link>
            <guid>https://velog.io/@hjun-kr/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%B7%A8%EC%A4%80%EC%83%9D%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%84-%EA%B3%B5%EB%B6%80%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80-Ep.-1</guid>
            <pubDate>Thu, 07 May 2026 14:47:40 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가며-👋">들어가며 👋</h1>
<p>안녕하세요! 박홍준입니다.</p>
<p>지난번에 썼던 <a href="https://velog.io/@hjun-kr/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%B7%A8%EC%A4%80%EC%83%9D%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%84-%EA%B3%B5%EB%B6%80%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80-Ep.-0">개발자 취준생은 무엇을 공부해야 하는가?</a>가 많은 관심을 받아서, 최대한 빠르게 다음 내용을 준비했습니다.</p>
<p>지난 포스트에서 주니어가 가져야 할 기본 소양을 가볍게 훑어봤다면, 이번에는 <strong>회사가 주니어에게 실제로 바라는 것</strong>을 좀 더 깊게 다뤄보려고 합니다.</p>
<p>내용이 꽤 길지만, 끝까지 읽고 나면 분명히 얻어가는 게 있을 거라고 자신 있게 말씀드릴 수 있습니다.</p>
<h1 id="기본적인-cs-상식-🖥️">기본적인 CS 상식 🖥️</h1>
<p>여러분은 CS 상식이 풍부하다고 생각하시나요?</p>
<p>사람마다 다르겠지만, 저는 꽤 안다고 생각했습니다. 그런데 막상 면접에서 질문이 나오니 제대로 된 대답을 못 했습니다... 😭</p>
<h3 id="네트워크">네트워크</h3>
<p>네트워크를 공부할 때 대부분 이런 순서로 시작합니다.</p>
<ul>
<li>OSI 7계층</li>
<li>TCP vs UDP 차이</li>
<li>HTTP 구조</li>
</ul>
<p>저도 똑같이 공부했습니다. 그런데 면접에서 이런 질문을 받았습니다.</p>
<blockquote>
<p>사용자가 웹사이트에 접속하면 어떤 일이 일어날까요?</p>
</blockquote>
<blockquote>
<p>그렇다면, 왜 서비스가 느린가요?</p>
</blockquote>
<p>이 질문에서 막히는 순간, 개념은 알지만 <strong>흐름을 이해하지 못한 사람</strong>이라는 걸 스스로 깨닫게 됩니다.</p>
<h4 id="전체-흐름">전체 흐름</h4>
<p>사용자가 브라우저에 URL을 입력하면 이런 일들이 순서대로 일어납니다.</p>
<ol>
<li>캐시 조회</li>
<li>DNS 조회</li>
<li>TCP 연결</li>
<li>HTTP 요청 전송</li>
<li>서버 처리</li>
<li>응답 반환</li>
<li>브라우저 렌더링</li>
</ol>
<p>단순히 나열하는 것 자체는 중요하지 않습니다. 각 단계에서 <strong>무슨 일이 왜 일어나는지</strong>를 이해하는 게 핵심입니다.</p>
<h4 id="1-캐시-및-hosts-파일-조회">1. 캐시 및 hosts 파일 조회</h4>
<p><code>velog.io</code>를 예시로 들어볼게요.</p>
<p>사용자가 URL을 입력하면, 컴퓨터는 곧바로 인터넷으로 나가지 않습니다. 먼저 브라우저 캐시를 확인하고, 없으면 OS의 hosts 파일을 봅니다.</p>
<p>실무에서 자주 겪는 실수 중 하나가, 개발 서버 IP를 hosts에 적어두고 잊어버려서 &quot;나만 접속이 안 되는&quot; 기현상을 마주하는 겁니다. 네트워크 문제를 디버깅할 때 <strong>내 컴퓨터 설정부터 확인해야 하는 이유</strong>입니다.</p>
<h4 id="2-dns-조회">2. DNS 조회</h4>
<p>캐시에 없다면 이제 DNS 서버에 물어봅니다.</p>
<p>통신사(ISP)가 제공하는 <strong>Local DNS</strong> 서버에도 IP 정보가 없다면, 아래와 같은 재귀적 조회(Recursive Query) 과정이 시작됩니다.</p>
<ol>
<li><strong>Local DNS → Root DNS</strong> : Root DNS는 정확한 IP는 모르지만, <code>.io</code>를 담당하는 TLD DNS 서버의 위치를 알려줍니다.</li>
<li><strong>Local DNS → TLD DNS</strong> : <code>.io</code> TLD DNS는 <code>velog.io</code>를 실제로 관리하는 Authoritative DNS 정보를 반환합니다.</li>
<li><strong>Local DNS → Authoritative DNS</strong> : 이 서버가 최종 IP 주소(A 레코드)를 응답합니다.</li>
<li>Local DNS는 결과를 클라이언트에 전달하고, 동시에 캐시에 저장해 이후 요청을 빠르게 처리합니다.</li>
</ol>
<p>이 과정에서 지연이 생기면 웹사이트는 시작조차 하지 못합니다. 대규모 서비스가 DNS 캐싱 전략을 정교하게 설계하는 이유가 여기에 있습니다.</p>
<h4 id="3-tcp-연결">3. TCP 연결</h4>
<p>IP 주소를 알아냈다면 이제 서버와 실제 통신을 시작해야 합니다. 하지만 인터넷은 신뢰할 수 없는 환경이기 때문에, 데이터를 보내기 전에 서로 통신 준비가 됐는지 확인하는 과정이 필요합니다.</p>
<p>TCP는 이를 위해 <strong>3-Way Handshake</strong>를 수행하며, 최소 1 RTT가 소요됩니다. 한국에서 미국 서버에 접속하는 경우, 이 단순한 &quot;인사&quot;만으로도 수백 밀리초의 지연이 생길 수 있습니다.</p>
<p>그래서 현대 네트워크에서는 <strong>Keep-Alive</strong>로 연결을 유지하거나, UDP 기반의 <strong>QUIC(HTTP/3)</strong> 프로토콜로 연결 과정 자체를 줄이려는 시도를 합니다.</p>
<h4 id="4-http-요청과-서버-처리">4. HTTP 요청과 서버 처리</h4>
<p>연결이 수립되면, 이제 그 통로 위로 실제 <strong>HTTP Request</strong>가 전달됩니다.</p>
<p>요청에는 어떤 작업인지 나타내는 <strong>Method(GET, POST 등)</strong>, 데이터 형식 등을 정의하는 <strong>Header</strong>, 그리고 실제 전송 데이터인 <strong>Body</strong>가 포함됩니다.</p>
<p>서버에 도착한 요청은 애플리케이션 계층의 영역으로 넘어갑니다. 웹 서버(Nginx, Apache)가 요청을 받고, WAS(Tomcat, Spring 등)가 비즈니스 로직을 처리하며 필요에 따라 DB를 조회합니다. 우리가 흔히 말하는 &quot;백엔드&quot;가 바로 이 구간입니다.</p>
<p>이 흐름 속에서 반드시 이해해야 할 개념이 있습니다. 바로 HTTP의 <strong>Stateless</strong> 특성입니다.</p>
<p>HTTP는 기본적으로 상태를 유지하지 않습니다. 요청과 응답이 끝나면 서버는 이전 요청을 전혀 기억하지 않습니다. 방금 로그인 요청을 보냈더라도, 다음 요청이 들어오면 서버는 그 사용자가 누구인지 알 수 없습니다.</p>
<p>이 특성 때문에 &quot;로그인 상태 유지&quot; 같은 기능을 구현하려면 별도의 장치가 필요합니다.</p>
<p>가장 전통적인 방식은 <strong>쿠키 + 세션</strong>입니다. 서버가 사용자 정보를 세션 저장소에 보관하고, 클라이언트에는 세션 ID를 쿠키로 내려줍니다. 이후 요청마다 쿠키를 함께 보내면 서버가 사용자를 식별할 수 있습니다.</p>
<p>반면 <strong>JWT</strong> 같은 토큰 기반 방식은 서버가 상태를 저장하지 않습니다. 서버가 사용자 정보를 담은 토큰을 만들어 클라이언트에 넘기면, 클라이언트가 이후 요청에 토큰을 포함시키고 서버는 토큰 자체를 검증해 사용자를 식별합니다.</p>
<p>두 방식의 차이는 결국 <strong>&quot;상태를 어디에 저장하느냐&quot;</strong> 입니다. 세션은 서버에, JWT는 클라이언트에 위임합니다. 그리고 이 선택은 확장성, 보안, 서버 부하 등 다양한 문제로 이어집니다.</p>
<p>&quot;로그인이 풀린다&quot;는 단순해 보이는 현상도, 원인은 쿠키 만료일 수도, 세션 저장소 문제일 수도, 토큰 유효기간 문제일 수도 있습니다. 이 흐름을 이해하지 못하면 문제를 단편적으로만 바라보게 되고, 근본적인 해결에 도달하기 어렵습니다.</p>
<h4 id="5-응답-반환과-브라우저-렌더링">5. 응답 반환과 브라우저 렌더링</h4>
<p>비즈니스 로직 처리가 끝나면 <code>200 OK</code>와 함께 HTML, JSON 등의 데이터가 클라이언트로 돌아옵니다. 하지만 데이터가 도착했다고 끝이 아닙니다. 사용자가 웹사이트를 &quot;본다&quot;고 느끼려면, 브라우저가 이 데이터를 화면에 그리는 <strong>렌더링 과정</strong>이 필요합니다.</p>
<p>브라우저는 HTML을 한 줄씩 읽으며 <strong>DOM 트리</strong>를 만들고, CSS를 해석해 <strong>CSSOM 트리</strong>를 구성합니다. 두 개가 합쳐져 <strong>Render Tree</strong>가 완성되고, 각 요소의 크기와 위치를 계산하는 <strong>Layout</strong>, 실제 색상을 입히는 <strong>Paint</strong> 과정을 거쳐 비로소 화면이 나타납니다.</p>
<p>여기서 주니어 개발자가 놓치기 쉬운 포인트가 하나 있습니다. 바로 <strong>렌더링 차단</strong> 문제입니다.</p>
<p>HTML 중간에 무거운 <code>&lt;script&gt;</code> 태그가 있으면, 브라우저는 HTML 파싱을 멈추고 그 파일을 다운로드하고 실행하는 데 집중합니다. 네트워크 속도가 아무리 빨라도 자바스크립트가 최적화되어 있지 않으면 사용자는 한참 동안 흰 화면만 보게 됩니다. 결국 사용자 경험(UX)은 <strong>네트워크와 프론트엔드 최적화가 만나는 지점</strong>에서 결정됩니다.</p>
<p>다시 처음 질문으로 돌아가서,</p>
<blockquote>
<p>그렇다면, 왜 서비스가 느린가요?</p>
</blockquote>
<p>이 질문에 대한 답은 하나로 단정 지을 수 없습니다. DNS 지연일 수도, TCP 핸드셰이크일 수도, 서버 처리 시간일 수도, 렌더링 차단일 수도 있습니다. 병목이 어느 단계에서 발생하는지 먼저 분석하는 접근이 필요합니다.</p>
<p><strong>나쁜 답변:</strong> &quot;서버가 느린 것 같습니다.&quot;</p>
<p><strong>좋은 답변:</strong> &quot;DNS 지연 여부, TCP 핸드셰이크 시간, 서버 응답 시간을 단계별로 확인한 뒤, 병목 지점을 찾아 적절한 조치를 취해야 합니다.&quot;</p>
<p>결국 중요한 건 <strong>전체 흐름을 이해하고, 각 단계를 유기적으로 연결해서 실무자의 시각으로 바라보는 것</strong>입니다.</p>
<h1 id="언어에-대한-수준-높은-이해-💡">언어에 대한 수준 높은 이해 💡</h1>
<p>제가 가장 좋아하는 Java를 예로 들어볼게요.</p>
<blockquote>
<p>Java를 어느 정도 하시는 것 같으세요? 상, 중, 하로 나눈다면요?</p>
</blockquote>
<p>저는 &quot;중상 정도 하는 것 같습니다&quot;라고 답했습니다. 클래스 작성, 상속, 인터페이스 사용, 서버 개발까지 해봤으니까요.</p>
<p>그런데 이걸 한 방에 무너뜨리는 질문이 있었습니다.</p>
<blockquote>
<p>Java는 왜 객체지향을 지향하나요?</p>
</blockquote>
<p>이 질문에서 막히는 순간, Java를 <strong>사용만 해봤지 이해하지 못했다</strong>는 걸 깨닫게 됩니다.</p>
<h4 id="java의-탄생-배경">Java의 탄생 배경</h4>
<p>Java가 등장하기 전, C와 C++은 성능은 강력했지만 치명적인 약점이 있었습니다. 바로 <strong>플랫폼 종속성</strong>입니다. Windows에서 짠 코드를 Linux에서 돌리려면 다시 컴파일하거나 코드를 수정해야 했습니다.</p>
<p>Java는 이 문제를 <strong>JVM(Java Virtual Machine)</strong> 으로 해결했습니다. &quot;Write Once, Run Anywhere(WORA)&quot;라는 슬로건처럼, 개발자는 운영체제에 상관없이 Java 코드만 작성하면 됩니다. 각 OS에 맞는 JVM이 알아서 해석하고 실행해주기 때문입니다.</p>
<h4 id="java는-왜-객체지향oop을-선택했나">Java는 왜 객체지향(OOP)을 선택했나?</h4>
<p>Java가 설계될 당시, 소프트웨어 규모는 급격히 커지고 있었습니다. 수만 줄이 얽힌 환경에서 절차지향(C언어 스타일)은 작은 수정 하나가 전체 시스템을 무너뜨리는 결과를 초래했습니다.</p>
<p>Java는 이를 해결하기 위해 현실 세계를 투영한 <strong>객체(Object)</strong> 단위를 강제합니다. Java에서 거의 모든 것이 클래스로 시작하는 이유는, 개발자가 자연스럽게 &#39;부품화&#39;된 사고를 하도록 유도해 복잡한 시스템을 안전하게 관리하기 위해서입니다.</p>
<h4 id="포인터가-없는-이유">포인터가 없는 이유</h4>
<p>C++ 개발자들이 Java로 넘어올 때 가장 당황했던 건 <strong>포인터(Pointer)의 부재</strong>와 가비지 컬렉션(GC)이었습니다. 직접 메모리에 접근할 수 없다는 게 자유를 빼앗기는 것처럼 느껴졌기 때문이죠.</p>
<p>하지만 Java 설계자들의 생각은 달랐습니다. &quot;인간은 실수한다.&quot; 메모리를 직접 관리하다 발생하는 누수(Memory Leak)나 잘못된 주소 접근은 시스템 전체를 멈추게 하는 치명적인 원인이 됩니다. Java는 이 권한을 개발자에게서 빼앗아 JVM에 맡겼습니다.</p>
<p>이 &#39;안전장치&#39; 덕분에 Java는 거대한 금융 시스템이나 국가 기간망처럼 <strong>&quot;절대 죽으면 안 되는 시스템&quot;</strong> 의 표준이 되었습니다.</p>
<h4 id="현대-java의-진화">현대 Java의 진화</h4>
<p>최근 Java는 순수 객체지향에만 머물지 않습니다. Java 8 이후로 함수형 프로그래밍(Lambda, Stream API)을 도입하며 현대적인 변화를 꾀하고 있습니다.</p>
<p>왜일까요? 객체지향이 거대한 구조를 잡기에는 탁월하지만, 데이터를 처리하는 짧고 간결한 로직에서는 코드가 불필요하게 길어지는(Boilerplate) 단점이 있었기 때문입니다.</p>
<p>이제 Java는 &quot;구조는 객체지향으로 단단하게, 데이터 처리는 함수형으로 간결하게&quot;라는 하이브리드 전략으로 진화하고 있습니다.</p>
<blockquote>
<p>Java는 왜 객체지향을 지향하나요?</p>
</blockquote>
<p><strong>나쁜 답변:</strong> &quot;상속과 다형성을 쓰기 위해서입니다.&quot;</p>
<p><strong>좋은 답변:</strong> &quot;거대하고 복잡한 비즈니스 로직을 안정적으로 운영하고, 유지보수 비용을 최소화하기 위해 객체라는 부품 단위로 사고하도록 강제하는 설계를 선택했기 때문입니다.&quot;</p>
<p>결국 중요한 건 <strong>문법이 아니라 철학</strong>입니다. 도구의 사용법은 금방 익힐 수 있지만, 그 도구가 만들어진 의도를 파악하고 적재적소에 활용하는 능력은 치열한 고민 끝에 얻어지는 것입니다.</p>
<h1 id="상태를-파악하는-능력-🔍">상태를 파악하는 능력 🔍</h1>
<p>데이터를 다루다 보면 이런 상황을 반드시 마주칩니다.</p>
<p>&quot;분명히 저장했는데 왜 이 값이 틀렸지?&quot; &quot;버그 재현이 안 된다.&quot; &quot;로컬에서는 되는데 프로덕션에서만 이상하다.&quot;</p>
<p>저도 이런 상황에서 코드만 뚫어지게 쳐다봤습니다. 로직이 문제라고 생각했고, 코드를 수정하고 또 수정했습니다. 그런데 면접에서 이런 질문을 받았습니다.</p>
<blockquote>
<p>버그를 발견했을 때 가장 먼저 무엇을 확인하나요?</p>
</blockquote>
<p>이 질문에서 막히는 순간, 문제를 해결하는 방법은 알지만 <strong>문제를 제대로 들여다보는 방법은 모르는 사람</strong>이라는 걸 깨닫게 됩니다.</p>
<h4 id="코드가-아니라-상태를-먼저-봐야-한다">코드가 아니라 상태를 먼저 봐야 한다</h4>
<p>많은 주니어 개발자가 버그를 만나면 곧바로 코드로 뛰어듭니다. 로직을 의심하고, 조건문을 고치고, 값을 출력해봅니다. 그런데 정작 가장 중요한 걸 놓칩니다. 바로 <strong>&quot;지금 이 데이터가 어떤 상태인가&quot;</strong> 입니다.</p>
<p>데이터에는 항상 상태가 있습니다. 주문 하나를 예로 들면 <code>주문 접수 → 결제 대기 → 결제 완료 → 배송 준비 → 배송 중 → 배송 완료</code>라는 흐름이 있습니다. 그런데 막상 버그가 생겼을 때 &quot;지금 이 주문이 어떤 상태에 있는가&quot;를 먼저 확인하지 않고 코드를 고치면, 원인도 모른 채 해결책을 찾는 삽질이 시작됩니다.</p>
<p>이건 비즈니스 로직에만 해당하는 이야기가 아닙니다. 데이터베이스 레코드의 값, 캐시에 저장된 데이터, 외부 API가 반환한 응답, 트랜잭션의 커밋 여부 — 시스템의 모든 요소에는 지금 이 순간의 상태가 있습니다. 그리고 대부분의 버그는 <strong>개발자가 예상한 상태와 실제 상태가 달라서</strong> 생깁니다.</p>
<h4 id="상태를-파악하지-못하면-생기는-일">상태를 파악하지 못하면 생기는 일</h4>
<p>실무에서 자주 보이는 패턴이 있습니다.</p>
<p>사용자가 &quot;결제가 안 됐는데 돈은 빠져나갔어요&quot;라고 신고합니다. 주니어 개발자는 결제 로직 코드를 열어봅니다. 코드는 멀쩡합니다. 그런데 DB를 열어보면 주문 상태는 <code>결제 대기</code>인데 실제 결제는 이미 완료된 상태입니다. 외부 결제 API는 성공 응답을 보냈지만, 그 응답을 받아 상태를 업데이트하는 과정에서 타임아웃이 났던 것입니다.</p>
<p>코드가 아니라 <strong>데이터의 상태를 먼저 봤다면</strong> 훨씬 빨리 원인을 찾을 수 있었습니다.</p>
<p>이처럼 상태를 파악하지 못하면 두 가지 문제가 생깁니다. 원인 파악이 느려지고, 잘못된 지점을 고치게 됩니다. 반대로 상태를 먼저 확인하는 습관이 있는 개발자는 버그 앞에서도 침착합니다. 어디를 봐야 하는지 알기 때문입니다.</p>
<h4 id="상태를-설계하는-것도-실력이다">상태를 설계하는 것도 실력이다</h4>
<p>상태를 파악하는 능력은 디버깅에만 쓰이지 않습니다. 처음부터 상태를 잘 설계하는 것 자체가 실력입니다.</p>
<p>예를 들어 주문 상태를 <code>true/false</code> 두 가지로만 관리하는 설계와, <code>PENDING / PAID / SHIPPING / DELIVERED / CANCELLED</code> 처럼 명확한 상태값으로 관리하는 설계는 시스템의 복잡도를 완전히 다르게 만듭니다. 상태가 명확하게 정의되어 있으면 &quot;지금 이 데이터가 어느 단계에 있는지&quot;를 누구나 한눈에 알 수 있습니다. 상태 전이(State Transition)가 어디서 어떻게 일어나는지도 추적하기 쉽습니다.</p>
<p>반대로 상태가 모호하게 설계되어 있으면, 코드는 점점 복잡한 조건문으로 가득 차고, 새로운 케이스가 생길 때마다 예상치 못한 버그가 함께 따라옵니다.</p>
<h4 id="상태를-보는-눈을-키우는-방법">상태를 보는 눈을 키우는 방법</h4>
<p>상태를 파악하는 능력은 어느 날 갑자기 생기지 않습니다. 의식적으로 훈련해야 합니다.</p>
<p>버그를 만났을 때 코드를 열기 전에 먼저 이 질문을 던져보는 것입니다. &quot;지금 이 데이터는 어떤 상태인가? 내가 기대하는 상태와 실제 상태가 같은가?&quot; 로그를 확인하고, DB를 직접 조회하고, 외부 서비스의 응답을 확인합니다. 상태를 확인한 뒤에야 코드를 봅니다.</p>
<p>기능을 설계할 때도 마찬가지입니다. &quot;이 데이터가 가질 수 있는 상태는 무엇인가? 각 상태에서 허용되는 행동과 허용되지 않는 행동은 무엇인가?&quot; 를 먼저 정의하는 것입니다. 이 사고 과정이 자연스러워지면, 처음 설계 단계에서 이미 많은 버그를 미리 막을 수 있습니다.</p>
<p><strong>나쁜 답변:</strong> &quot;일단 로그를 찍어보고 코드에서 어디가 문제인지 찾아봅니다.&quot;</p>
<p><strong>좋은 답변:</strong> &quot;먼저 데이터의 현재 상태를 확인합니다. DB 레코드의 값, 캐시 상태, 외부 API 응답 이력 등을 보고 제가 기대하는 상태와 실제 상태가 어디서 달라졌는지를 특정합니다. 상태가 틀어진 지점을 찾은 뒤에 그 원인이 되는 코드를 봅니다.&quot;</p>
<p>결국 중요한 건 <strong>코드를 빨리 보는 것</strong>이 아닙니다. 데이터가 지금 어떤 상태인지를 먼저 이해하고, 그 상태가 왜 그렇게 됐는지를 역추적하는 능력입니다. 그 능력이 있는 개발자와 없는 개발자는, 똑같은 버그 앞에서 완전히 다른 속도로 움직입니다.</p>
<h1 id="테스트-코드-🧪">테스트 코드 🧪</h1>
<p>취업 준비 중이거나 주니어 개발자라면, 기능 구현에 급급해서 테스트 코드를 뒤로 미루는 경우가 많습니다. 하지만 실무는 다릅니다. 수만 건의 트래픽이 몰리고, 내가 짠 코드를 동료가 수정하기도 합니다. 테스트 코드가 없다면 &quot;이게 제대로 동작하는지&quot; 확인할 방법 자체가 사라집니다.</p>
<h3 id="테스트-코드는-선택이-아닌-필수">테스트 코드는 선택이 아닌 필수</h3>
<p>테스트 코드를 처음부터 작성하면 확실히 속도가 느려지는 느낌이 듭니다. 기능 하나에 테스트까지 포함하면 1.5배, 많게는 2배 가까이 시간이 걸리는 것 같기도 합니다.</p>
<p>하지만 이건 느려진 게 아닙니다. <strong>미래에 발생할 문제를 먼저 처리하는 것</strong>입니다.</p>
<p>버그는 언제 생길까요? 대부분 기능을 추가하거나 수정할 때 생깁니다. 그리고 그 버그를 찾는 데 드는 시간은 처음 구현할 때보다 훨씬 오래 걸립니다. 프로덕션에서 터지면 디버깅, 롤백, 핫픽스, 재배포까지 이어지며 몇 배의 비용으로 돌아옵니다.</p>
<h3 id="리팩토링">리팩토링</h3>
<p>개발을 하다 보면 이런 순간이 반드시 옵니다.</p>
<blockquote>
<p>&quot;이 구조 별로인데… 고치고 싶다.&quot;</p>
</blockquote>
<p>이때 대부분의 개발자는 리팩토링을 미루거나 최소한으로만 건드립니다. 이유는 간단합니다. <strong>망가질까 봐 무서워서</strong>입니다.</p>
<p>그런데 테스트 코드가 있다면 이야기가 달라집니다.</p>
<p>구조를 마음껏 바꾸고 나서 테스트를 돌리면 됩니다. 전부 통과하면 &quot;기존과 동일하게 동작한다&quot;는 걸 증명한 것이고, 실패한다면 &quot;여기서 문제가 생겼다&quot;는 걸 즉시 알 수 있습니다. 테스트 코드는 리팩토링의 <strong>안전망</strong>입니다.</p>
<p>테스트 코드가 없는 상태에서의 리팩토링은, 안전망 없이 줄타기하는 것과 같습니다. 실수하면 바로 떨어집니다.</p>
<h3 id="어떤-테스트를-작성해야-할까">어떤 테스트를 작성해야 할까?</h3>
<p>테스트는 크게 세 가지 단위로 나눌 수 있습니다.</p>
<p><strong>단위 테스트(Unit Test)</strong> 는 가장 작은 단위인 함수나 메서드 하나를 검증합니다. 외부 의존성을 Mock으로 대체하기 때문에 빠르고 독립적입니다. 테스트 코드의 대부분을 차지해야 합니다.</p>
<p><strong>통합 테스트(Integration Test)</strong> 는 여러 모듈이 함께 동작할 때를 검증합니다. 예를 들어 Service 레이어가 Repository를 거쳐 실제 DB와 연동되는 과정을 테스트합니다. 단위 테스트보다 느리지만, 모듈 간 연결 문제를 잡아냅니다.</p>
<p><strong>E2E 테스트(End-to-End Test)</strong> 는 실제 사용자 시나리오 전체를 검증합니다. &quot;회원가입 → 로그인 → 주문&quot;처럼 전체 흐름을 확인합니다. 가장 현실에 가깝지만 느리고 유지 비용이 높습니다.</p>
<p>세 가지를 모두 100% 커버하는 게 이상적이지만, 현실적으로는 <strong>단위 테스트를 탄탄히 하고, 핵심 흐름은 통합 테스트로 보완하는 전략</strong>이 효과적입니다.</p>
<h3 id="좋은-테스트-코드란">좋은 테스트 코드란?</h3>
<p>테스트 코드를 작성하다 보면 이런 의문이 생깁니다. &quot;일단 통과하면 좋은 테스트 아닌가요?&quot;</p>
<p>아닙니다. <strong>테스트 자체도 유지보수의 대상</strong>입니다. 나쁜 테스트 코드는 기능 코드보다 더 큰 짐이 될 수 있습니다.</p>
<p>좋은 테스트 코드의 조건을 간단히 정리하면 이렇습니다.</p>
<p>첫째, <strong>하나의 테스트는 하나만 검증합니다.</strong> 여러 동작을 한 테스트에 몰아넣으면, 실패했을 때 어디서 문제가 생겼는지 알기 어렵습니다.</p>
<p>둘째, <strong>테스트 이름만 봐도 무엇을 검증하는지 알 수 있어야 합니다.</strong> <code>testLogin()</code>보다 <code>로그인_성공시_토큰을_반환한다()</code>가 훨씬 명확합니다.</p>
<p>셋째, <strong>외부 의존성에 휘둘리지 않아야 합니다.</strong> DB가 꺼져 있어도, 외부 API가 응답하지 않아도 단위 테스트는 통과해야 합니다. 이를 위해 Mock과 Stub을 적극적으로 활용합니다.</p>
<p>넷째, <strong>테스트가 서로 독립적이어야 합니다.</strong> 테스트 실행 순서에 따라 결과가 달라지는 테스트는 신뢰할 수 없습니다.</p>
<h3 id="테스트-코드가-만들어-주는-문화">테스트 코드가 만들어 주는 문화</h3>
<p>테스트 코드의 가치는 단순히 버그를 줄이는 것에 그치지 않습니다.</p>
<p>팀 전체가 테스트를 작성하는 문화가 정착되면, 새로운 팀원이 합류했을 때도 &quot;이 코드가 어떻게 동작하는지&quot;를 테스트 코드를 통해 파악할 수 있습니다. 테스트 코드가 곧 <strong>살아있는 문서(Living Documentation)</strong> 가 되는 것입니다.</p>
<p>또한 CI/CD 파이프라인에 테스트를 연결해 두면, 누군가 코드를 올렸을 때 자동으로 검증이 이루어집니다. &quot;머지하고 나서 서버가 죽는&quot; 상황을 사전에 방지할 수 있습니다.</p>
<p>처음에는 번거롭게 느껴지더라도, 테스트 코드를 꾸준히 작성하다 보면 어느 순간 <strong>&quot;테스트 없이는 배포하기 불안하다&quot;</strong> 는 감각이 생깁니다. 그 감각이 생겼다면, 진짜 실무 개발자에 한 걸음 더 가까워진 것입니다.</p>
<h1 id="최신-기술에-대한-관심과-이해-🚀">최신 기술에 대한 관심과 이해 🚀</h1>
<p>&quot;저는 새로운 기술을 항상 공부합니다&quot;라고 자신 있게 말하는 분들이 많습니다.</p>
<p>저도 그랬습니다. 새 프레임워크가 나오면 튜토리얼을 따라 해보고, 컨퍼런스 영상을 찾아보고, 기술 블로그를 구독했습니다.</p>
<p>그런데 면접에서 이런 질문을 받았습니다.</p>
<blockquote>
<p>쿠버네티스는 왜 사용하나요?</p>
</blockquote>
<p>이 질문에서 막히는 순간, 기술을 <strong>따라가기만 했지 이해하지 못했다</strong>는 걸 깨닫게 됩니다.</p>
<h4 id="쿠버네티스가-나온-이유">쿠버네티스가 나온 이유</h4>
<p>쿠버네티스(Kubernetes, K8s)를 이해하려면, 그 이전 세계를 먼저 알아야 합니다.</p>
<p>컨테이너 기술인 Docker가 등장하면서 &quot;어디서든 동일하게 실행되는 환경&quot;이라는 문제가 해결됐습니다. 개발 환경과 운영 환경이 달라서 생기는 &quot;내 로컬에서는 됐는데요&quot;의 시대가 끝난 것입니다.</p>
<p>그런데 서비스가 커지면서 새로운 문제가 생겼습니다. 컨테이너가 수십, 수백 개로 늘어나자, 이걸 어떻게 배포하고, 어떻게 관리하고, 죽으면 어떻게 되살리느냐는 문제가 새로운 골칫거리가 됐습니다.</p>
<p><strong>쿠버네티스는 바로 이 문제를 해결하기 위해 만들어졌습니다.</strong> 컨테이너 오케스트레이션(Container Orchestration), 즉 수많은 컨테이너를 지휘하는 지휘자 역할을 합니다.</p>
<h4 id="쿠버네티스가-해결하는-것들">쿠버네티스가 해결하는 것들</h4>
<p><strong>자동 복구(Self-healing)</strong> 입니다. 컨테이너가 죽으면 자동으로 다시 띄웁니다. 새벽 3시에 서버가 다운됐을 때 개발자가 깨어나 수동으로 재시작할 필요가 없습니다.</p>
<p><strong>자동 스케일링(Auto-scaling)</strong> 입니다. 트래픽이 급증하면 컨테이너를 자동으로 늘리고, 트래픽이 줄면 다시 줄입니다. 명절 직전 쇼핑몰 트래픽이 폭발해도 쿠버네티스가 알아서 대응합니다.</p>
<p><strong>무중단 배포(Rolling Update)</strong> 입니다. 새 버전을 배포할 때 서비스를 내리지 않고 점진적으로 교체합니다. 사용자는 배포가 일어나는지조차 모릅니다.</p>
<p><strong>리소스 효율화</strong> 입니다. 여러 서버에 컨테이너를 최적으로 배치해 자원 낭비를 줄입니다.</p>
<h4 id="그렇다면-무조건-써야-할까">그렇다면 무조건 써야 할까?</h4>
<p>많은 주니어 개발자가 &quot;요즘 대세니까&quot;라는 이유로 기술을 선택하려는 경향이 있습니다. 쿠버네티스도 마찬가지입니다.</p>
<p>하지만 쿠버네티스는 학습 곡선이 가파르고, 운영 복잡도가 높습니다. 컨테이너가 몇 개 없는 작은 서비스에 도입하면 오히려 배보다 배꼽이 더 커집니다.</p>
<p>새로운 기술을 이해하는 진짜 능력은 &quot;이걸 쓸 줄 안다&quot;가 아니라, <strong>&quot;언제 써야 하고 언제 쓰지 말아야 하는지를 안다&quot;</strong> 입니다.</p>
<p><strong>나쁜 답변:</strong> &quot;컨테이너를 관리하기 위해서 씁니다.&quot;</p>
<p><strong>좋은 답변:</strong> &quot;Docker로 컨테이너화한 서비스가 늘어날수록, 배포 자동화·장애 복구·스케일링을 수동으로 관리하는 비용이 급격히 커집니다. 쿠버네티스는 이 문제를 오케스트레이션으로 해결합니다. 다만 소규모 서비스에는 운영 복잡도 대비 효과가 낮을 수 있어, 팀의 규모와 서비스 트래픽 패턴을 고려해 도입 여부를 판단해야 합니다.&quot;</p>
<p>결국 중요한 건 <strong>기술의 이름을 아는 것</strong>이 아니라, <strong>그 기술이 어떤 문제를 풀기 위해 만들어졌는지를 이해하는 것</strong>입니다. 그 이해가 있어야 적재적소에 쓸 수 있고, 면접에서도 설득력 있게 말할 수 있습니다.</p>
<h1 id="프로덕션-문제를-인식하고-해결하는-능력-🔥">프로덕션 문제를 인식하고 해결하는 능력 🔥</h1>
<p>코드를 로컬에서 잘 돌리는 것과, 실제 서비스를 운영하는 것은 완전히 다른 세계입니다.</p>
<p>저도 처음엔 그 차이를 몰랐습니다. 로컬에서 잘 되면 끝인 줄 알았습니다. 그런데 프로덕션은 달랐습니다. 사용자가 몰리고, 예상치 못한 순서로 요청이 들어오고, 내가 통제할 수 없는 외부 변수가 넘쳐납니다.</p>
<p>면접에서 이런 질문을 받았습니다.</p>
<blockquote>
<p>서버에 트래픽이 갑자기 몰리는 상황이라면 어떻게 대처하겠습니까?</p>
</blockquote>
<p>이 질문에서 막히는 순간, <strong>실제 서비스를 운영해본 경험이 없거나, 있더라도 깊이 생각해보지 않았다</strong>는 걸 드러내게 됩니다.</p>
<h4 id="트래픽이-몰리면-어디서-터질까">트래픽이 몰리면 어디서 터질까?</h4>
<p>트래픽 급증이라는 현상 뒤에는 다양한 원인이 숨어 있습니다. 단순히 &quot;서버 한 대 더 추가하면 되지 않나?&quot;라고 생각하기 쉽지만, 실제로는 각 계층에서 다른 방식으로 문제가 발생합니다.</p>
<p><strong>애플리케이션 서버</strong> 가 먼저 한계에 달할 수 있습니다. 요청을 처리하는 스레드 풀이 꽉 차면 새로운 요청이 대기열에 쌓이다가 타임아웃이 납니다.</p>
<p><strong>데이터베이스</strong> 가 병목이 될 수도 있습니다. 요청이 몰리면 DB 커넥션 풀이 고갈되고, 쿼리가 느려지며, 결국 전체 시스템이 느려집니다.</p>
<p><strong>외부 API 호출</strong> 이 문제일 수도 있습니다. 내 서버는 괜찮아도 연동하는 결제 서버나 외부 서비스가 응답을 못 하면 내 서비스도 함께 먹통이 됩니다.</p>
<p>이걸 이해하지 못하면, 문제를 해결하는 게 아니라 <strong>문제를 옆으로 옮기는 것</strong>에 그치게 됩니다.</p>
<h4 id="단계별-대응-전략">단계별 대응 전략</h4>
<p>트래픽 급증 상황에서 실무에서는 크게 두 가지 관점으로 접근합니다. <strong>즉각 대응(단기)</strong> 과 <strong>구조적 개선(장기)</strong> 입니다.</p>
<p>즉각 대응에서 가장 먼저 확인해야 할 것은 <strong>모니터링 지표</strong>입니다. CPU, 메모리, DB 커넥션 수, 응답 시간(Latency), 에러율을 보면 병목이 어느 계층에 있는지 빠르게 파악할 수 있습니다. 감으로 판단하는 게 아니라, 데이터를 보고 판단하는 것입니다.</p>
<p>병목이 애플리케이션 서버라면 <strong>수평 확장(Scale-out)</strong> 으로 인스턴스를 늘리고 로드 밸런서를 통해 트래픽을 분산합니다. DB가 병목이라면 읽기 요청을 <strong>Read Replica</strong> 로 분산하거나, <strong>Redis 같은 캐시 레이어</strong>를 도입해 DB 직접 조회를 줄입니다.</p>
<p>그래도 요청이 감당이 안 된다면 <strong>Rate Limiting</strong>으로 특정 사용자나 IP의 요청 수를 제한하거나, 핵심 기능만 남기고 나머지는 임시로 비활성화하는 <strong>서킷 브레이커(Circuit Breaker)</strong> 패턴을 적용합니다.</p>
<p>구조적 개선은 당장의 불을 끈 뒤에 합니다. 왜 이 상황이 발생했는지 돌아보고, 처음부터 트래픽을 견딜 수 있는 아키텍처를 설계하는 것입니다. 비동기 처리, 메시지 큐, 데이터베이스 샤딩, CDN 도입 등이 이 단계에서 논의됩니다.</p>
<h4 id="문제를-인식하는-능력이-먼저다">문제를 인식하는 능력이 먼저다</h4>
<p>흥미롭게도, 많은 주니어 개발자가 &quot;어떻게 해결하느냐&quot;보다 <strong>&quot;문제가 생겼다는 걸 인식하는 것&quot;</strong> 에서 더 많이 막힙니다.</p>
<p>프로덕션에서 문제가 생겼을 때 가장 먼저 해야 하는 것은 &quot;일단 코드 고치기&quot;가 아닙니다. 로그를 보고, 메트릭을 확인하고, 언제부터 문제가 시작됐는지 파악하는 것입니다. 그래야 원인 없이 해결책을 찾는 삽질을 피할 수 있습니다.</p>
<p>실무에서는 이 과정을 <strong>장애 대응 플로우(Incident Response)</strong> 라고 부릅니다. 문제를 감지하고, 영향 범위를 파악하고, 임시 대응으로 서비스를 복구한 뒤, 근본 원인을 분석하고 재발 방지책을 마련하는 일련의 과정입니다.</p>
<p>이 흐름을 머릿속에 가지고 있는 사람과 그렇지 않은 사람은, 장애 상황에서 완전히 다르게 행동합니다.</p>
<p><strong>나쁜 답변:</strong> &quot;서버를 늘리고, 캐시를 적용하겠습니다.&quot;</p>
<p><strong>좋은 답변:</strong> &quot;먼저 모니터링 지표를 확인해 병목 계층을 특정합니다. 애플리케이션 서버가 문제라면 수평 확장과 로드 밸런싱을, DB가 문제라면 캐시 레이어 도입과 Read Replica를 검토합니다. 즉각 대응으로 서비스를 안정화한 뒤, 근본 원인을 분석해 아키텍처 수준의 개선 방향을 논의합니다.&quot;</p>
<p>결국 중요한 건 <strong>해결책을 외우는 것</strong>이 아닙니다. 시스템 전체를 흐름으로 이해하고, <strong>문제가 어디서 왜 생겼는지를 추론하는 능력</strong>입니다. 그 능력이 있어야 처음 보는 문제 앞에서도 당황하지 않고 접근할 수 있습니다.</p>
<h1 id="커뮤니케이션-🤝">커뮤니케이션 🤝</h1>
<p>개발자는 코드만 잘 짜면 된다고 생각하시나요?</p>
<p>저도 그렇게 생각했습니다. 기술 실력만 높이면 모든 게 해결될 줄 알았습니다. 그런데 막상 팀 프로젝트를 경험하면서, 코드보다 대화가 더 많은 문제를 만들어낸다는 걸 깨달았습니다.</p>
<p>면접에서 이런 질문을 받았습니다.</p>
<blockquote>
<p>팀원과 기술적인 방향이 맞지 않는다면 어떻게 하겠습니까?</p>
</blockquote>
<p>이 질문에서 막히는 순간, 혼자 코딩하는 법만 알고 <strong>팀에서 일하는 법은 모르는 사람</strong>이라는 걸 드러내게 됩니다.</p>
<h4 id="기술적-의견-충돌은-반드시-생긴다">기술적 의견 충돌은 반드시 생긴다</h4>
<p>팀에서 일하다 보면 의견이 엇갈리는 순간이 반드시 옵니다.</p>
<blockquote>
<p>&quot;REST API로 하자 vs GraphQL로 하자&quot; &quot;모놀리식으로 가자 vs MSA로 나누자&quot; &quot;Redis 캐시 레이어를 추가하자 vs 일단 DB 쿼리 최적화부터 하자&quot;</p>
</blockquote>
<p>이런 상황에서 많은 주니어 개발자가 두 가지 실수 중 하나를 합니다. 자신이 맞다고 강하게 밀어붙이거나, 반대로 아무 의견 없이 시니어를 그냥 따르거나. 둘 다 좋지 않습니다.</p>
<h4 id="의견-충돌을-다루는-방법">의견 충돌을 다루는 방법</h4>
<p>기술 논쟁에서 중요한 건 &quot;누가 맞냐&quot;가 아닙니다. <strong>&quot;지금 우리 상황에서 무엇이 더 적합하냐&quot;</strong> 입니다.</p>
<p>의견이 충돌할 때 가장 먼저 해야 할 것은, 서로의 주장 뒤에 있는 <strong>근거와 맥락을 이해하는 것</strong>입니다. 상대방이 왜 그 기술을 선택하려는지, 어떤 문제를 해결하려는 건지를 먼저 물어보는 것입니다.</p>
<p>예를 들어 동료가 GraphQL을 도입하자고 주장한다면, 단순히 &quot;REST가 더 익숙하니 REST로 하자&quot;고 반박하기 전에 이렇게 접근해 보는 것입니다.</p>
<p>&quot;GraphQL을 선택하려는 이유가 클라이언트 요청 유연성 때문인가요, 아니면 오버페칭 문제를 해결하려는 건가요? 우리 서비스 규모에서 그 문제가 지금 당장 중요한 우선순위인지 같이 검토해봐도 될까요?&quot;</p>
<p>이렇게 접근하면 논쟁이 아닌 <strong>대화</strong>가 됩니다. 그리고 대화가 되어야 더 나은 결정에 도달할 수 있습니다.</p>
<h4 id="그래도-결론이-나지-않는다면">그래도 결론이 나지 않는다면</h4>
<p>충분히 논의했는데도 의견이 좁혀지지 않는다면, 두 가지 방법을 사용할 수 있습니다.</p>
<p>하나는 <strong>데이터로 판단하는 것</strong>입니다. 작은 PoC(Proof of Concept)를 만들어서 실제로 비교해보거나, 유사한 서비스의 사례를 찾아보는 것입니다. 주장이 아니라 근거로 이야기하면 감정 소모 없이 결론에 도달할 수 있습니다.</p>
<p>다른 하나는 <strong>팀의 결정을 따르는 것</strong>입니다. 충분히 내 의견을 전달했는데도 팀이 다른 방향을 선택했다면, 기꺼이 그 결정에 힘을 보태야 합니다. 불만을 품고 대충 구현하거나, 나중에 &quot;그래서 내가 그렇게 하지 말라고 했잖아요&quot;는 팀을 망가뜨리는 태도입니다.</p>
<p>의견을 말하는 것과 결정을 따르는 것, 이 둘을 분리해서 생각할 수 있어야 합니다.</p>
<h4 id="커뮤니케이션은-기술이다">커뮤니케이션은 기술이다</h4>
<p>커뮤니케이션은 성격이나 성향의 문제가 아닙니다. 연습하고 발전시킬 수 있는 <strong>기술</strong>입니다.</p>
<p>좋은 커뮤니케이터는 자신의 주장을 명확하게 전달하면서도, 상대방의 맥락을 이해하려는 노력을 멈추지 않습니다. 기술적인 논의를 할 때는 항상 트레이드오프를 인식하고, &quot;이 선택이 가져오는 장단점이 무엇인지&quot;를 함께 이야기합니다.</p>
<p><strong>나쁜 답변:</strong> &quot;제 의견을 충분히 설명한 뒤, 팀 결정에 따르겠습니다.&quot;</p>
<p><strong>좋은 답변:</strong> &quot;먼저 동료가 그 방향을 선택하려는 이유와 맥락을 이해하려고 합니다. 이후 서로의 근거를 바탕으로 트레이드오프를 함께 검토하고, 필요하다면 작은 PoC로 검증해봅니다. 충분한 논의 후에도 결론이 나지 않는다면 팀의 결정을 따르되, 구현 과정에서 최선을 다합니다.&quot;</p>
<p>결국 중요한 건 <strong>내가 옳은 것</strong>이 아니라, <strong>팀이 더 나은 결정을 내릴 수 있도록 기여하는 것</strong>입니다.</p>
<h1 id="마치며-💌">마치며 💌</h1>
<p>CS 상식, 언어 철학, 상태 파악, 테스트 코드, 최신 기술, 프로덕션 대응, 커뮤니케이션. 적고 나니 정말 많다는 생각이 드실 수도 있습니다. 저도 이 글을 쓰면서 &quot;나는 이걸 다 잘하고 있나?&quot;를 수없이 되물었습니다.</p>
<p>그런데 솔직히 말씀드리면, 이 모든 걸 완벽하게 갖춘 주니어는 없습니다. 시니어들도 알고 있습니다. 그래서 면접에서 이런 질문들을 하는 겁니다. 완벽한 답을 듣기 위해서가 아니라, <strong>이걸 어떻게 생각하는 사람인지</strong>를 보기 위해서입니다.</p>
<p>개념만 외운 사람과, 왜 그렇게 동작하는지를 고민해본 사람은 같은 질문 앞에서 완전히 다른 대답을 합니다. 면접관은 그 차이를 금방 알아챕니다.</p>
<p>저 역시 아직 부족한 점이 많습니다. 이 글을 쓰면서 다시 공부한 내용도 있고, 솔직히 더 파고들어야 할 부분도 보였습니다. 그래도 이렇게 한 자 한 자 정리하는 과정이 저를 조금씩 단단하게 만들어주고 있다고 믿습니다.</p>
<p>여러분도 오늘 이 글을 읽은 것으로 끝내지 말고, 하나씩 직접 고민해보시길 바랍니다. &quot;왜 서비스가 느린가요?&quot;라는 질문에 스스로 답해보고, 내가 쓰는 언어가 왜 그렇게 설계됐는지 한 번만 더 생각해보는 것. 그 작은 습관의 차이가 6개월 뒤, 1년 뒤를 완전히 다르게 만듭니다.</p>
<p>저의 이 시리즈가 같은 고민을 하는 취준생 여러분께 조금이나마 위로와 방향표가 되었으면 좋겠습니다. 다음 글에서 또 뵙겠습니다!!</p>
<pre><code>지치지 말고, 오늘도 한 걸음만 더 나아가 봅시다. 🍀</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[개발자 취준생은 무엇을 공부해야 하는가? (Ep. 0)]]></title>
            <link>https://velog.io/@hjun-kr/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%B7%A8%EC%A4%80%EC%83%9D%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%84-%EA%B3%B5%EB%B6%80%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80-Ep.-0</link>
            <guid>https://velog.io/@hjun-kr/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%B7%A8%EC%A4%80%EC%83%9D%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%84-%EA%B3%B5%EB%B6%80%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80-Ep.-0</guid>
            <pubDate>Fri, 24 Apr 2026 02:45:14 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가며-👋">들어가며 👋</h1>
<p>안녕하세요! 개발자로써의 첫발을 내딛기 위해 준비하고 있는 박홍준입니다. (ps. 전의 글은 전부 아카이빙 해두고 지웠습니다.. ㅠㅠ 나중에 천천히 모든 글을 다시 풀어보겠습니다.)</p>
<p>감사하게도 여러 회사를 준비하며 서류 전형에서는 긍정적인 결과가 있었지만, 면접에서는 아쉬운 결과가 많았습니다.</p>
<p>이런 문제를 바탕으로 제가 받은 피드백으로 개발자 취준생은 &#39;<strong>무엇을 공부</strong>&#39;해야 하는가에 대해 이야기하고 기록을 남겨보겠습니다! 이 시리즈가 많은 개발자 취준생에게 도움이 되기를 바라겠습니다.</p>
<h1 id="기업이-ai대신-왜-주니어를-뽑아야-하는가-🍯">기업이 AI대신 왜 주니어를 뽑아야 하는가? 🍯</h1>
<p>많은 취준생이 &quot;이제 코딩은 AI가 다 하는데, 신입 개발자가 설 자리가 있을까?&quot;라며 불안해합니다. 하지만 역설적으로 AI의 등장은 주니어에게 새로운 기회이자, 우리가 왜 필요한지를 증명할 수 있는 무대가 되었습니다.</p>
<h3 id="what무엇이-아닌-why왜를-고민하는-존재">What(무엇)이 아닌 Why(왜)를 고민하는 존재</h3>
<p>AI는 방대한 데이터를 바탕으로 정답을 내놓는 데 특화되어 있습니다. 하지만 그 기술이 왜 우리 서비스에 필요한지, 비즈니스 로직에는 어떤 영향을 미칠지 판단하지 못합니다. 주니어 개발자는 기술의 &#39;동작&#39;뿐만 아니라 &#39;맥락&#39;을 이해하도록 노력해야 합니다. AI가 짜준 코드가 왜 이렇게 동작하는지 집요하게 파고드는 주니어는 AI가 절대 대체 할 수 없습니다.</p>
<h3 id="소프트-스킬">소프트 스킬</h3>
<p>개발은 혼자 하는 것이 아닌, 팀이 함께하는 예술과도 같습니다. 팀이 만든 코드를 리뷰하고, 기술적 충돌이 발생했을때 소통하며, 조직의 목표에 대해 헌신하는 태도는 오직 사람만이 가질 수 있습니다. AI는 코드를 짤 순 있지만, 팀의 사기를 높이거나 기업의 비전을 함께 고민할 순 없습니다.</p>
<h3 id="예측-불가능한-성장">예측 불가능한 성장</h3>
<p>AI의 성능은 모델의 파라미터에 갇혀 있지만, 주니어의 성장 가능성은 무한합니다. 오늘 가르쳐준 것을 스펀지처럼 흡수해 내일 바로 적용하고, 실패를 통해 얻은 교훈으로 조직에 새로운 에너지를 불어넣는 모습. 기업은 완성된 AI보다, 기업과 함께하며 그 회사의 &#39;색깔&#39;을 입고 성장할 미래의 핵심 인재를 원합니다.</p>
<h1 id="주니어에게-좋은-코드를-기대하지-않는다-🫵">주니어에게 좋은 코드를 기대하지 않는다 🫵</h1>
<p>AI가 나오고 많은 취준생들은 <strong>좋은 코드</strong>에 집착합니다. 하지만 시니어들은 주니어에게 좋은 코드를 기대하지 않습니다. 경험이 부족한데 좋은 코드가 나올 수 없고, 좋은 코드란 실제 프로덕션에서 검증된 결과물 입니다.</p>
<h3 id="회사가-주니어에게-바라는-것">회사가 주니어에게 바라는 것</h3>
<ul>
<li>기본적인 CS 상식 (Ex. 네트워크, 운영체제, 데이터베이스, 보안, 알고리즘)</li>
<li>언어에 대한 수준높은 이해 (Ex. Java언어가 지향하는 바는 무엇인가요?)</li>
<li>프로덕션에서 실 문제 제기가 가능하고, 문제를 해결할 수 있는 능력 (Ex. 서버에 트래픽이 몰리는 상황에서 어떻게 대처할 것인가요?)</li>
<li>테스트 코드 (Ex. 테스트 코드에 대해서 어떻게 생각하나요?)</li>
<li>클라우드 (Ex. 클라우드가 왜 필요하다고 생각하나요?)</li>
<li>커뮤니케이션 (Ex. 팀원과의 기술적인 문제로 맞지 않는다면 어떤 방향을 선택할 것인가요?)</li>
<li>최신기술에 대한 관심과 이해 (Ex. 쿠버네티스는 왜 사용하나요?)</li>
</ul>
<p>너무 많은거 아니냐고요? 음.. 어쩌죠 이게 현실인걸.... 😭😭</p>
<h1 id="주니어는-지속적인-기록으로-성장을-보여줘야-한다-🍀">주니어는 지속적인 기록으로 성장을 보여줘야 한다 🍀</h1>
<p>&quot;실력은 이미 뽑힌 뒤에 보여주는 것이고, 뽑히기 전에는 <strong>태도</strong>를 보여줘야 합니다.&quot;</p>
<p>시니어들이 주니어에게 좋은 코드를 기대하지 않는다면, 그들은 무엇으로 우리를 판단할까요? 바로 &#39;어제보다 오늘 더 성장했는가&#39;입니다. 그리고 그 성장을 유일하게 객관적으로 증명할 수 있는 도구가 바로 <strong>기록</strong>입니다.</p>
<h3 id="기록은-해결의-로드맵">기록은 해결의 로드맵</h3>
<p>우리가 겪는 수많은 에러와 해결 과정은 휘발성이 강합니다. 몇 시간동안 고민해서 고친 버그도 1주일이 지나면 잊어버립니다. 하지만 이를 기록으로 남기는 순간, &#39;<strong>문제 해결 프로세스</strong>&#39;가 됩니다. 면접관은 얼마나 넓고 다양한 지식을 아느냐보다, 모르는 문제를 만났을 때 문제를 정의하고 해결하는 과정을 보고 싶어 합니다.</p>
<h3 id="꾸준함이라는-무기">꾸준함이라는 무기</h3>
<p>일주일에 한번, 아니 한 달에 한 번이라도 내가 배운것과 고민한 것을 남기는 습관은 생각보다 중요합니다. 1년뒤 기록을 되돌아 봤을때, 초기의 미숙한 글과 지금의 깊이 있는 고민이 대비되는 모습 자체가 여러분의 <strong>성장 곡선</strong> 그 자체가 됩니다. &quot;저는 열심히 하는 사람입니다&quot;, &quot;저는 이렇게 성장했습니다&quot;라는 100마디의 말보다, 1년간 쌓인 포스팅이 훨씬 더 신뢰감을 줍니다.</p>
<h3 id="나의-기술로-만드는-법">나의 기술로 만드는 법</h3>
<p>누군가에게 설명할 수 있을 정도로 글을 정리하는 과정에서 지식의 빈틈이 발견됩니다. 내가 이해했다고 생각하는 개념도 남들에게 설명하고 글로 정리하는 과정에서 &#39;정확히 모른다&#39;는 것을 깨닫게 됩니다. 저 또한 이 시리즈를 쓰면서 부족했던 기본기들을 다시금 채우고 있습니다.</p>
<p><del>사실 저도 블로그 쓰기 너무 힘듭니다,, ㅠㅡㅠ</del></p>
<h1 id="마치며-💌">마치며 💌</h1>
<p>여기 쓰인 이야기들이 말하는 <strong>한가지 기준</strong>이 있습니다. <strong>개발자로써 빠른 성장</strong>을 할 수 있는 사람입니다.</p>
<p>사실 저도 이 글을 쓰면서 스스로를 많이 돌아보게 되었습니다. 면접에서 아쉬운 결과를 마주할 때마다 &quot;내가 부족해서일까?&quot;라는 생각에 잠기기도 했지만, 결국 그 부족함을 채워가는 이 과정이 저를 다음 단계로 이끌어줄 것이라 믿습니다.</p>
<p>블로그를 쓰는 것도, CS 기본기를 다시 다지는 것도 결코 쉽지 않은 과정입니다. 저도 가끔은 다 내려놓고 싶을 만큼 힘들 때가 있습니다. 하지만 제가 남긴 이 작은 기록들이 누군가에게는 힌트가 되고, 저에게는 단단한 뿌리가 될 것입니다.</p>
<p>여러분은 지금 어떤 기록을 남기고 계신가요? 우리의 기록이 쌓여 실력이 되고, 그 실력이 결국 우리가 꿈꾸던 기회로 연결될 것이라 확신합니다. 비록 지금은 면접 탈락이라는 쓴맛을 보고 있을지라도, 우리의 성장 곡선은 지금 이 순간에도 위를 향하고 있으니까요.</p>
<p>저의 이 시리즈가 같은 고민을 하는 취준생 여러분께 조금이나마 위로와 방향표가 되었으면 좋겠습니다. 다음 글에서 또 뵙겠습니다!!</p>
<pre><code>지치지 말고, 오늘도 한 걸음만 더 나아가 봅시다. 🍀</code></pre>]]></description>
        </item>
    </channel>
</rss>