<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>l-wanderer01.log</title>
        <link>https://velog.io/</link>
        <description>항상 왜?를 생각하는 개발자</description>
        <lastBuildDate>Sun, 01 Feb 2026 06:35:40 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>l-wanderer01.log</title>
            <url>https://velog.velcdn.com/images/l-wanderer01/profile/761375ed-5055-43b9-bea8-878c6c25d6f5/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. l-wanderer01.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/l-wanderer01" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[엔터프라이즈 애플리케이션의 표준, Spring Boot]]></title>
            <link>https://velog.io/@l-wanderer01/%EC%97%94%ED%84%B0%ED%94%84%EB%9D%BC%EC%9D%B4%EC%A6%88-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%98-%ED%91%9C%EC%A4%80-Spring-Boot</link>
            <guid>https://velog.io/@l-wanderer01/%EC%97%94%ED%84%B0%ED%94%84%EB%9D%BC%EC%9D%B4%EC%A6%88-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%98-%ED%91%9C%EC%A4%80-Spring-Boot</guid>
            <pubDate>Sun, 01 Feb 2026 06:35:40 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<p>백엔드 개발의 핵심은 <strong>데이터를 어떻게 안전하게 저장하고, 효율적으로 처리하여, 사용자에게 전달할 것인가</strong> 에 있습니다. 다른 백엔드 개발 프레임워크들(Django, Node.js)등도 있지만, Spring Boot가 채택이 되는 이유는 대규모 엔터프라이즈 환경에서의 확실한 안정성과 정형화된 규칙, 예외처리, 인증 및 인가에서 강력한 기능을 제공하기 때문입니다. 따라서 여러 사람이 동일한 프로젝트를 개발하여도 일률적인 규칙에 의해 개발이 가능하고 안정적인 서비스 제공이 가능합니다.</p>
<h2 id="1️⃣-견고한-기초-객체지향과-설계-원칙-java-oop--solid">1️⃣ 견고한 기초: 객체지향과 설계 원칙 (Java OOP &amp; SOLID)</h2>
<p>Spring Boot에 대해 알아보기 전, Spring Boot의 근간이 되는 언어인 Java의 중요 개념에 대해 알아보고자 합니다.
Java는 객체지향 언어로 그 특징으로 캡슐화, 추상화, 다형성, 상속을 가집니다.</p>
<ul>
<li><strong>캡슐화</strong> : 객체 외부에서 내부에 어떤 필드를 가지고 있는지 알지 못하게 하는 것입니다.</li>
<li><strong>추상화</strong>: 메서드의 동작을 선언하지 않고 메서드명만 선언할 수 있는 것입니다.</li>
<li><strong>다형성</strong>: 같은 메서드명을 가졌지만, 다양한 동작을 하는 메서드를 가질 수 있는 것입니다.</li>
<li><strong>상속</strong>: 자식 객체는 부모 객체의 필드 및 메서드를 가질 수 있는 것입니다.</li>
</ul>
<p>캡슐화는 접근 제어자, 추상화는 abstract class, interface, 다형성은 method overriding, method overloading, 상속은 extends를 이용해 구현합니다.</p>
<p>다음으로 객체지향의 5대 원칙인 SOLID 원칙입니다.</p>
<ul>
<li><strong>SRP</strong> (단일 책임 원칙)
: 하나의 클래스는 하나의 책임만을 가져야 한다.</li>
<li><strong>OCP</strong> (개방 폐쇄 원칙)
: 클래스는 확장에는 열려 있고, 수정에는 닫혀 있어야 한다.</li>
<li><strong>LSP</strong> (리스코프 치환 원칙)
: 부모 클래스를 자식 클래스가 대체할 수 있어야 한다.</li>
<li><strong>ISP</strong> (인터페이스 치환 원칙)
: 하나의 범용적 인터페이스가 아닌 여러 개의 인터페이스로 구현해야 한다.</li>
<li><strong>DIP</strong> (의존 역전 원칙)
: 고수준 모듈은 저수준 모듈에 의존하면 안된다.
(상위 모듈은 하위 모듈이 아닌 인터페이스에 의존해야 한다.)</li>
</ul>
<p>결국 Spring Boot는 위와 같은 Java의 객체 지향 특징과 원칙에 따라 구현되어야 합니다.</p>
<h2 id="2️⃣-생산성의-혁신-spring-boot-아키텍처-core--container">2️⃣ 생산성의 혁신: Spring Boot 아키텍처 (Core &amp; Container)</h2>
<p>Spring Boot를 공부하기 전 Spring Boot의 전신인 Spring과 어떤 차이점이 있는지 알아보고자 합니다.
Spring은 동일하게 백엔드 개발에 사용되던 Java 기반 프레임워크입니다. 하지만, 이를 사용하기 위해선 개발에 사용되던 시간보다 설정(환경, 의존성, 서버 실행 등)에 사용되는 시간이 많았습니다.. 이 점 때문에 백엔드 개발의 강력한 프레임워크이지만 사용에 어려움이 있었고, 이를 해결하기 위해 탄생한 것이 Spring Boot입니다.</p>
<ul>
<li>“설정하는 시간을 줄이고, 개발에 할애하는 시간을 늘이자!”</li>
</ul>
<p>Spring Framework의 복잡한 설정을 Spring Boot는 IoC와 DI, Bean을 통해 자동화합니다.</p>
<p><strong>IoC</strong>는 제어의 역전으로 객체의 생성, 생명 주기 관리, 의존성 주입 등을 개발자가 아닌 Spring 컨테이너가 관리하도록 위임하는 것을 의미합니다. 따라서 코드의 결합도를 낮추고, 유연한 아키텍처와 테스트 가능한 구조로 만들 수 있습니다.</p>
<p>이처럼 컨테이너가 외부에서 객체를 관리해주기 때문에 개발자는 특정 기술이나 프레임워크에 종속되지 않은 순수한 자바 객체(POJO (Plain Old Java Object))만으로 비즈니스 로직을 구현할 수 있습니다. 이를 통해 객체지향 원리에 충실한 설계를 가능하게 하며, 단위 테스트를 더욱 용이하게 만듭니다.</p>
<p><strong>의존성 주입(DI)</strong>은 이렇게 관리되는 POJO 객체들이 의존하는 다른 객체를 직접 생성하는 것이 아닌 Spring 컨테이너에서 주입받는 방식을 말합니다. Spring에서는 생성자 주입을 사용해 불변성을 보장하고 순환 참조를 방지하는 것이 권장됩니다.</p>
<p>생성자 주입 방식이 권장되는 이유는 주입 받을 객체를 final로 선언해 불변성을 보장하고, 의존성 주입을 하지 않거나 순환 참조가 발생했을 시, 컴파일 타임에 이를 확인하고 수정이 가능합니다. 또한, 한눈에 파악하기 용이합니다.</p>
<p>DI가 적용되는 흐름은 다음 사진과 같습니다.
<img src="https://velog.velcdn.com/images/l-wanderer01/post/2d5bc095-36c7-4693-8b65-94090a7cc0f4/image.png" alt="">
Spring Boot는 객체를 Bean으로 등록하여 객체를 생성하고 삭제합니다. 객체 생성 후 @PostConstruct로 초기화하고, 소멸 전 @PreDestroy로 리소스를 정리합니다. 이렇게 Bean으로 등록해 객체의 생성과 삭제를 맡기는 이유는 개발자가 객체를 생성한 후 삭제하는 로직을 작성하지 않았을 시, 메모리 누수가 발생하여 서비스 중단되는 일이 발생할 수 있는데 이를 미연에 방지하기 위해 Spring Boot는 IoC를 통해 개발자가 아닌 프레임워크에 이 일을 위임합니다.</p>
<p>Bean 생명 주기 흐름은 아래와 같습니다.
<img src="https://velog.velcdn.com/images/l-wanderer01/post/3b9f4400-7dc7-44d5-957c-1f38ac9a70d2/image.png" alt=""></p>
<p>Spring Boot에선 서비스의 환경(Configuration)을 application.yml 파일을 이용해 설정하는데 동일한 서비스를 여러 환경에서 구동할 수 있도록 Spring Boot는 Profile 기능을 제공합니다. 이를 통해 개발 환경, 테스트 환경, 운영 환경을 코드 변경없이 전환할 수 있습니다.</p>
<p>이는 application-dev.yml, application-test.yml, application-prod.yml 과 같이 환경 설정 파일을 생성하고 각각의 운영환경을 활용해 구동할 수 있는 쉘 스크립트를 짠다면, Profile 기능을 활용해 환경 전환이 가능합니다.</p>
<h2 id="3️⃣-웹-서비스의-표준-rest-api와-mvc-web-layer">3️⃣ 웹 서비스의 표준: REST API와 MVC (Web Layer)</h2>
<p>Spring Boot는 요청을 중앙에서 처리하는 <strong>Dispatcher Servlet</strong>을 중심으로 Controller(요청 처리), Service(비즈니스 로직), Repository(DB 접근)로 역할을 분리해 유지보수성을 높입니다. 이러한 MVC 패턴은 데이터 처리, 화면 처리, 제어 로직을 분리하는 것이 목적입니다.</p>
<p><strong>Controller</strong>는 사용자 요청의 입구로 파라미터를 받고 비즈니스 로직을 호출한 뒤 결과를 반환합니다.</p>
<p><strong>Model</strong>은 Controller가 로직 수행 결과를 View에 전달할 때 사용하는 데이터 저장소입니다.</p>
<p><strong>View</strong>는 사용자에게 보여줄 최종 화면(HTML, JSON 등)을 처리합니다.</p>
<p>계층 간의 분리를 통해 각 컴포넌트는 맡은 역할에 집중하는데 Service는 실질적 비즈니스 로직을 수행하고 Repository는 DB(데이터 저장소)에 접근하는 통로, 이후 설명할 Mapper는 자바 객체와 SQL 쿼리를 연결하는 역할을 수행합니다.</p>
<p>이렇게 분리된 구조를 기반으로 웹 요청을 처리하고, 데이터를 전달하며 화면을 렌더링합니다.</p>
<p>Spring MVC 요청 흐름은 아래와 같습니다.</p>
<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/5324ad07-98ed-45bc-850d-0255b6750633/image.png" alt=""></p>
<p>웹 어플리케이션 개발을 위해선 먼저 Spring Boot의 의존성에 spring-boot-starter-web을 추가해야합니다. <strong>spring-boot-starter-web</strong>은 웹 어플리케이션에 필요한 모든 라이브러리를 한 번에 포함하는 starter로 내장 서버인 Tomcat을 포함해 프론트엔드와의 소통을 위한 RESTful API, 웹 MVC, JSON, 예외 처리, 로깅 등을 지원합니다.</p>
<p>Spring Boot에서 처리된 자원들을 프론트엔드에 서빙하기 위해서 일종의 규약이 필요한데 그것이 <strong>RESTful API</strong> 입니다. RESTful API는 <strong>자원</strong>을 명사(URI)로 <strong>행위</strong>를 HTTP 메서드(GET, POST 등)로 표현하는 아키텍처 스타일입니다.</p>
<p>GET은 조회, POST는 생성, PUT은 전체 수정, PATCH는 부분 수정, DELECT는 삭제를 의미합니다. 이렇게 표현된 규약에 따라 API를 사용하여 프론트엔드와의 통신을 하고 의미를 명확하게 전달합니다.</p>
<p>프론트엔드와의 원활한 통신을 위해 주로 JSON 포맷을 사용합니다. Spring Boot에서 처리한 자원을 Controller에서 @RestController 어노테이션을 이용하면 자원을 XML또는 JSON 형식으로 전달합니다.</p>
<p>이렇게 서빙된 API를 프론트엔드 측에서 쉽게 확인하고 테스트할 수 있도록 돕는 도구를 OAS(OpenAPI Specification)이라고 합니다. 현업에서 많이 사용되는 것은 Swagger로 Spring Boot의 springdoc-openapi 의존성을 활용해 코드로부터 API 명세서(Swagger UI)를 자동 생성해 프론트엔드와 효율적으로 협업합니다.</p>
<h2 id="4️⃣-데이터-중심-개발-jpa와-트랜잭션-persistence-layer">4️⃣ 데이터 중심 개발: JPA와 트랜잭션 (Persistence Layer)</h2>
<p>기존의 Java를 이용한 개발에서는 Java에서 SQL을 직접 실행하여 DB와 연동하는 방식인 JDBC를 이용해 개발했습니다. JdbcTemplate 또는 MyBatis를 이용해 SQL을 직접 작성하고 모든 쿼리를 직접 작성해야했으나 이 어려움을 JPA (Java Persistence API)라는 새로운 기술을 통해 단순화하였습니다. JPA는 SQL을 직접 작성하는 JDBC 방식에서 벗어나, 객체 중심으로 데이터를 다룹니다.</p>
<p><strong>JPA</strong> (ORM, Object-Relational Mapping)를 이용해 Java 객체와 DB 테이블을 매핑합니다. @Entity, @Id, @Column 등의 어노테이션으로 스키마를 정의하고, JpaRepository 인터페이스를 통해 기본적으로 CRUD를 자동합니다.</p>
<p><strong>spring-boot-starter-data-jpa</strong> 의존성을 gradle 파일에 추가하고 JPA 및 DB 연결 정보를 application.yml 파일에 작성해 사용합니다.</p>
<p>JPA는 Spring Data JPA가 제공하는 인터페이스인 <strong>JPA Repository</strong>를 이용해 id를 이용한 데이터 추출(findById), 모든 데이터 조회(findAll) 등의 데이터베이스 조작이 가능합니다. 기본 제공되는 메서드 이외의 개발자가 메서드 키워드를 이용해 원하는 데이터를 가지고오는 메서드를 생성할 수 있습니다.</p>
<p>By(뒤에 오는 조건 필드 지정), And/Or(조건 연결), Like(SQL의 like와 동일한 기능)등의 <strong>메서드 키워드</strong>를 메서드에 작성하면 해당 동작을 하는 쿼리문이 자동 적용됩니다. 복잡한 조건 또는 특정 데이터 형태의 조회가 필요하다면 <strong>@Query</strong> 어노테이션을 통해 JPQL 또는 Native SQL을 이용한 쿼리문 작성도 가능합니다. 하지만 더 나아가 복잡한 동적 쿼리나 타입 안정성이 필요할 때는 자바 코드로 쿼리를 짜는 QueryDSL을 도입하는 것이 일반적입니다.</p>
<p>@Query 어노테이션을 사용하면 직접 쿼리문을 작성할 수 있어 편리하지만, 쿼리가 문자열 형태이기 떄문에 오타가 발생해도 컴파일 시점에 잡아내기 어렵다는 단점이 있습니다. 이를 해결하기 위해 QueryDSL을 함께 사용합니다.</p>
<blockquote>
<p>즉, 간단한 조회는 메서드 이름 규칙으로 해결하고, 복잡한 동적 쿼리는 QueryDSL을 사용해 타입 안정성을 확보합니다.</p>
</blockquote>
<p>JPA는 자동으로 <strong>트랜잭션</strong>을 관리해줍니다. 서비스 계층에서 @Transactional 어노테이션을 통해 데이터 정합성을 보장합니다. 예외 발생 시 자동으로 롤백(Rollback)되며, 서비스 계층에서 트랜잭션의 범위를 설정하는 것이 중요합니다. JPA에서 트랜잭션이 커밋되는 시점은 기본적으로 트랜잭션을 시작한 메서드가 성공적으로 종료될 때입니다.</p>
<p>트랜잭션이 시작되면 JPA는 <strong>영속성 컨텍스트</strong>라는 논리적 영역을 생성하고 엔티티는 해당 영역에, SQL은 <strong>쓰기 지연 SQL 저장소</strong>에 저장됩니다. 트랜잭션이 종료될 때, 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화(Flush)하고 쓰기 지연 저장소의 내용을 불러와 SQL을 실행합니다.</p>
<h2 id="5️⃣-rest-api-보안과-인증-jwt-json-web-token">5️⃣ REST API 보안과 인증: JWT (JSON Web Token)</h2>
<p>기존의 세션 기반 인증 방식은 서버 메모리에 사용자 상태를 저장해야 하므로 사용자가 늘어날수록 서버에 부담이 가고, 여러 대의 서버를 운영할 때 세션을 공유하기 까다롭다는 단점이 있습니다. 이를 해결하기 위해 토큰 기반 인증 방식인 <strong>JWT</strong>를 도입해 확장성을 확보합니다.</p>
<p>JWT는 인증에 필요한 정보들을 JSON 객체에 담아 암호화한 후, 이를 클라이언트에게 전달하는 토큰입니다. 서버는 사용자의 인증 상태를 저장하지 않고(Stateless) 클라이언트가 요청을 보낼 때마다 토큰을 함께 전달하면 서버는 토큰의 유효성만 검증하기에 서버 확장 시, 매우 유리합니다. 또한 토큰 자체에 사용자 식별 정보나 권한 등 필요한 데이터가 포함되어 있어 별도의 데이터 조회가 줄어듭니다.</p>
<p>JWT는 .을 구분자로 사용해 세 부분으로 구성됩니다.</p>
<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/66345475-874b-4549-aaab-68b29b775a20/image.png" alt=""></p>
<p>Header에는 토큰의 타입(JWT)과 사용된 암호화 알고리즘 정보(HS256, RS256 등)가 담깁니다.</p>
<p>Payload에는 실제 전달할 사용자 정보인 Claims가 담겨있고 이 안에는 사용자 ID, 토큰 만료 시간 등을 포함합니다. Payload를 수정하더라도 서버의 Secret Key가 없으면 올바른 서명을 생성할 수 없기에 보안은 유지되지만, Payload는 암호화가 아닌 인코딩된 정보이기에 누구나 정보를 볼 수 있습니다. 
따라서 비밀번호와 같은 민감 정보는 절대 담지 않도록 주의가 필요합니다.</p>
<p>마지막으로 Signature엔 헤더와 페이로드를 합친 후 서버만이 가진 Secret Key로 암호화한 값입니다. 이를 통해 토큰이 중간에 변조되지 않았음을 증명합니다.</p>
<p>동작 방식은 클라이언트가 로그인을 성공하면 서버는 JWT를 생성해 응답합니다. 이후 클라이언트는 HTTP 요청 헤더에 해당 토큰을 실어 보내고, 서버는 Secret Key를 이용해 서명을 검증하는 과정을 통해 사용자를 인증합니다.</p>
<p><em>참고)</em> CORS(Cross-Origin Resource Sharing)
웹 브라우저는 보안 상의 이유로 다른 Origin의 리소스 요청을 기본적으로 차단하는데 프론트엔드와의 연결을 위해 서버 측에서 이를 풀어줘야합니다. 이때 사용하는 것이 CORS로 서버가 ‘내 자원을 특정 외부 도메인에서 접근해도 된다’고 허용해주는 것입니다.</p>
<h2 id="6️⃣-안정성과-운영-횡단-관심사와-모니터링-aspects--ops">6️⃣ 안정성과 운영: 횡단 관심사와 모니터링 (Aspects &amp; Ops)</h2>
<p>Spring boot에선 비즈니스 로직 외의 공통 관심사를 분리하고 시스템 상태를 진단합니다.</p>
<p><strong>AOP</strong> (관점 지향 프로그래밍)를 이용해 로깅, 보안, 트랜잭션 등 반복되는 코드 즉, 공통 관심사를 <strong>Aspect</strong>로 모듈화하여 핵심 로직과 분리합니다. 이는 핵심 비즈니스 로직을 오염시키지 않으면서도 시스템 전반에 일관된 운영 정책을 적용할 수 있게 합니다.</p>
<blockquote>
<p>OOP는 ‘기능’ 단위로 클래스를 구현한다면, AOP는 ‘공통 관심사(Aspect)’를 모듈화해 코드 중복을 줄이고 유지보수가 쉽도록 구현합니다.</p>
</blockquote>
<p>이는 별도의 설정 없이 spring-boot-starter-aop 의존성만 추가한다면 바로 사용 가능합니다. 이때 제공되는 어노테이션은 @Aspect, @Advice, @JoinPoint, @Pointcut 등이 있습니다.</p>
<p>서버 운영 측면에서 사용자가 잘못된 값을 입력하지 않도록 사용자의 <strong>입력값 검증</strong> 또한 중요합니다. 검증 과정 없이 잘못된 데이터가 시스템으로 들어오면, 예기치 못한 오류나 데이터베이스에 원치 않는 값이 저장될 수 있기 때문입니다. 이를 위해 자바 표준 어노테이션인 @Valid 또는 Spring이 제공하는 @Validation 어노테이션을 사용합니다.</p>
<p><em>참고)</em> @Valid는 일반적인 간단한 객체 검증에 사용되고, @Validation은 그룹을 지정해 상황별로 다르게 검증할 때 사용합니다.</p>
<p>@NotNull, @NotBlank, @Max, @Email 등을 사용하여 입력값 검증 로직을 DTO에 선언적으로 적용하고, 잘못된 데이터 유입을 @Valid를 이용해 컨트롤러단에서 즉시 차단합니다. 검증에 실패하면 서버는 400 Bad Request를 클라이언트에게 반환합니다.</p>
<blockquote>
<p>Service 계층이 아닌 Controller에서 검증하는 이유는 Service에서 데이터를 처리하기 전 잘못된 데이터를 가능한 한 시스템의 가장 바깥쪽에서 처리하기 위해 Controller에서 받자마자 검증을 거칩니다. 또한 전역적 예외 처리를 통해 일관된 형식의 응답을 클라이언트에게 보내주기 용이하기 때문입니다.</p>
</blockquote>
<p>Spring Boot는 <strong>예외 처리</strong> 설정을 하지 않았을 때 자동으로 BasicErrorController를 사용해 예외를 자동으로 처리합니다. 이때의 예외 처리를 사용자가 보기 편하도록 개발자가 커스텀할 수 있습니다.</p>
<p>@ControllerAdvice를 사용하여 예외 처리를 전역적으로 중앙화하고, @ExceptionHandler를 이용해 클라이언트에게 일관된 에러 응답을 제공합니다.</p>
<p>예외 처리의 우선순위는 컨트롤러 내의 @ExceptionHandler, @ControllerAdvice 내의 @ExceptionHandler, ResponseStatusException, 스프링 부트의 기본 예외 처리 순으로 처리됩니다.</p>
<p>Spring Boot는 애플리케이션의 상태 확인, 시스템 모니터링, 관리, 운영 자동화 등(서비스가 잘 돌아가고 있는가를 확인)을 위한 운영용 엔드 포인트를 <strong>Actuator</strong>라는 모듈로 제공합니다.</p>
<p>application.yml 파일에서 설정을 통해 /actuator/health, /actuator/metrics 등의 엔드포인트를 통해 애플리케이션의 운영 상태를 실시간으로 모니터링합니다.</p>
<p>뿐만 아니라, Spring Boot에서는 강력한 <strong>로깅 기능</strong>도 제공합니다. 애플리케이션 로그 관리가 중요한 이유는 문제 진단 및 원인 분석에 용이하고, 시스템 모니터링 및 운영 효율화 측면에서 반드시 필요합니다. Spring Boot는 로그 시스템을 자동으로 구성해줘 초기 설정만 한다면 바로 로그 관리가 가능합니다.</p>
<p>SLF4J는 자바 생태계에서 가장 널리 사용되는 로그 파사드(Facade) 라이브러리입니다. 직접 로그를 기록하지 않고 내부적으로 실제 로깅 구현체에 위임하는 방식을 취하고 있습니다.</p>
<p>여기서 로그 파사드란 여러 로깅 프레임워크를 일관된 API로 감싸서 사용할 수 있게 해주는 중간 계층을 의미합니다. 만약 Logback이라는 로그 파사드를 사용하다가 Log4j로 변경하고자 할 때, 코드 변경 없이 build.gradle 또는 pom.xml에서 의존성만 교체한다면 변경 가능합니다.</p>
<h2 id="결론">결론</h2>
<p>지금까지 살펴본 백엔드 개발의 흐름은 단순한 코드 작성을 넘어, <strong>객체지향 원칙</strong>이라는 단단한 기초 위에 <strong>Spring Boot</strong>라는 효율적인 도구를 얹고, <strong>JPA</strong>와 <strong>JWT</strong>, <strong>AOP</strong>와 같은 현대적인 기술 스택을 조화롭게 결합하는 과정입니다.</p>
<p>결국 백엔드 개발자에게 요구되는 역량은 특정 기술의 단순한 사용법 숙지가 아니라, <strong>&quot;왜 이 기술이 도입되었는가?&quot;</strong> 에 대한 본질적인 이해를 바탕으로 최적의 아키텍처를 설계하는 능력입니다. 이 글에서 다룬 핵심 개념들은 견고하고 확장 가능한 서비스를 구축하기 위한 든든한 이정표가 되어줄 것입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자원 서빙을 위한 핵심 언어, Java]]></title>
            <link>https://velog.io/@l-wanderer01/%EC%9E%90%EC%9B%90-%EC%84%9C%EB%B9%99%EC%9D%84-%EC%9C%84%ED%95%9C-%ED%95%B5%EC%8B%AC-%EC%96%B8%EC%96%B4-Java</link>
            <guid>https://velog.io/@l-wanderer01/%EC%9E%90%EC%9B%90-%EC%84%9C%EB%B9%99%EC%9D%84-%EC%9C%84%ED%95%9C-%ED%95%B5%EC%8B%AC-%EC%96%B8%EC%96%B4-Java</guid>
            <pubDate>Sat, 31 Jan 2026 05:52:38 GMT</pubDate>
            <description><![CDATA[<p><strong>서비스를 개발한다면 어떤 플로우로 짜야할까?</strong>
<img src="https://velog.velcdn.com/images/l-wanderer01/post/3adbd0e1-983c-4db7-9859-36b01a345b27/image.png" alt="">
<strong>프록시 서버</strong>: 사용자의 요청을 받음 (진입점) → “내부와 외부를 구분”
<strong>NAT</strong>: 백엔드에서 처리한 응답을 사용자에게 return 해주는 서버
<strong>프론트엔드</strong>: 사용자와 가까운 쪽 (Vue.js)
<strong>백엔드</strong>: 자원이 있는 쪽 (Spring boot)</p>
<ul>
<li>자원(Resource): 서비스를 이용하기 위한 정보 처리 및 사용자 정보 등</li>
</ul>
<p><strong>Java</strong>는 자원을 다루는 백엔드 개발을 위한 Spring Boot의 초석이 되는 핵심 언어입니다. 특히 대규모 트래픽과 복잡한 비즈니스 로직을 처리해야 하는 엔터프라이즈 환경에서, Java는 엄격한 예외 처리, 객체지향 설계 등 견고한 기능을 통해 시스템의 높은 안정성과 유지보수성을 보장해 줍니다.</p>
<p>Java : “역할을 가진 객체들을 조합하는 언어”</p>
<ol>
<li>Java 프로그램은 클래스 단위로 구성</li>
<li>클래스는 객체로 만들어서 사용</li>
<li>Main은 프로그램의 시작점</li>
<li>패키지는 클래스의 논리적 위치<ul>
<li>패키지: 클래스의 논리적 묶음</li>
</ul>
</li>
<li>Import로 다른 클래스를 연결</li>
<li>디렉토리 구조가 중요</li>
</ol>
<p><strong>클래스</strong>
앞선 Java 설명에 대한 내용을 보면 클래스에 대한 여러 언급이 있고, Java 프로그램 작성 시 class를 먼저 선언하고 시작합니다.
<img src="https://velog.velcdn.com/images/l-wanderer01/post/48401216-a6c9-4fb9-9aee-7f785a9018bb/image.png" alt=""></p>
<p>클래스의 개념이 탄생하기 전에는 서비스를 개발하기 위해 기존 방식대로 구조를 기능과 데이터로 나누어 개발을 진행 한 후, 구현하려 했으나 정상적으로 서비스가 요구사항 제대로 구현되지 않았습니다.</p>
<p>따라서 클래스의 개념이 탄생하게 되었고, java는 클래스 기반 언어이기에 class를 먼저 선언 후 내용을 작성해 나갑니다.</p>
<p><strong>객체</strong>
작성한 클래스를 활용할 때 아래와 같이 ‘Animal a1 = new Dog();’와 같이 객체를 생성하게 됩니다.
<img src="https://velog.velcdn.com/images/l-wanderer01/post/c1e1d1ff-190e-430c-83cb-e086a51d8d46/image.png" alt="">
클래스를 이용해 객체를 생성할 때, 무작정 객체를 생성하게 되면 많은 객체를 생성하게 되고 결국 객체들이 많은 메모리를 차지해 메모리 오버플로우가 발생합니다. 따라서 객체를 생성하는 방식에 대한 고민을 하게되었고, 이에 객체 생성 패턴이 필요하게 되었습니다.
객체 생성 패턴에는 <strong>싱글톤, 팩토리, 빌더 패턴</strong>이 있습니다.</p>
<p>1️⃣ 싱글톤 (Singleton)
싱글톤 방식은 객체 생성을 1번만 하고 외부에서 new 연산자로 객체를 생성할 수 없도록 생성자를 private으로 만듭니다. 프로그램 내에 단 하나의 인스턴스만 존재해야할 때 사용하는 것이 좋습니다.
<img src="https://velog.velcdn.com/images/l-wanderer01/post/78c1e796-4523-40bd-b701-b2f622a9864e/image.png" alt=""></p>
<p>2️⃣ 팩토리 (Factory)
팩토리 방식은 객체 생성 로직을 한 곳에 모아서 관리하는 패턴입니다. New를 통해 객체 생성을 여러 곳에서 하지 않고, “무엇을 만들지”를 공장에 맡기는 방식으로 구현합니다. 따라서 객체 생성 책임을 분리하여 코드 결합도를 낮추고, 변경에 강하게 만들 수 있습니다.</p>
<p>이 패턴은 객체 생성 로직을 클라이언트 코드(사용하는 쪽)로부터 숨기고 싶거나, 조건(if/else)에 따라 생성해야 할 객체의 클래스가 달라질 때, 나중에 새로운 타입의 객체가 추가되어도 기존 코드를 수정하고 싶지 않을 경우 사용하면 좋습니다.
<img src="https://velog.velcdn.com/images/l-wanderer01/post/1c7770b4-709b-4392-b201-43e639c7a26e/image.png" alt=""></p>
<p>3️⃣ 빌더 (Builder)
빌더 패턴은 필드가 많거나 선택적 값이 많은 객체를 안전하게 생성하기 위한 패턴입니다.</p>
<p>만약 생성자 패턴으로 객체를 생성하게 된다면 순서가 틀려도 컴파일 에러가 발생하지 않는 운영 중 발견되는 최악의 버그가 발생할 수 있습니다. 이를 빌더 패턴을 통해 이름으로 값을 지정하게 된다면 이 문제를 해결할 수 있습니다.</p>
<p>이 패턴은 생성자 인자가 너무 많거나 필수 인자, 선택 인자가 섞여 있을 경우 등에 사용한다면 이점을 발휘할 수 있습니다. 
<img src="https://velog.velcdn.com/images/l-wanderer01/post/7abe412a-beb4-4fc0-9b73-3394666f1ff1/image.png" alt=""></p>
<ul>
<li>빌더 패턴을 사용한 객체 생성!
<img src="https://velog.velcdn.com/images/l-wanderer01/post/5ca9fb1c-0c47-4d55-9c22-0350ff8683fa/image.png" alt=""></li>
</ul>
<p>최종적으로 위 생성 패턴들을 이용했을 때, 불필요한 메모리 소모를 감소할 수 있습니다.</p>
<ul>
<li>참고</li>
</ul>
<ol>
<li>‘코드 중복, 의존성 증가, 테스트 어려움’의 문제를 객체 생성 패턴으로 해결할 수 있습니다.</li>
<li>생성한 객체 중 일부는 아래와 같이 객체를 닫아줘야 합니다.
<img src="https://velog.velcdn.com/images/l-wanderer01/post/2b42e0df-bbf4-44b8-8161-6b01d83d223f/image.png" alt=""></li>
</ol>
<p><strong>상속 / 다형성</strong>
객체 지향 프로그래밍에서 객체를 효율적으로 활용하기 위해 상속과 다형성을 사용합니다. 이 둘은 밀접하게 연관되어 있습니다.</p>
<p>먼저 상속(Inheritance)은 구조적인 재사용을 담당합니다. 부모 클래스에 공통 기능을 정의하고, 이를 상속받은 자식 클래스에서 차이점만 구현함으로써 코드 중복을 줄이고 계층 구조를 만듭니다.</p>
<p>다형성(Polymorphism)은 이 구조 위에서 동작하는 유연한 활용 기술입니다. 상속받은 여러 자식 객체들을 부모 타입 하나로 묶어서 관리하되, 실제 실행 시점(Runtime)에는 각 자식 객체에 맞는 고유한 동작(메서드)이 동적으로 실행되도록 합니다.</p>
<p><strong>빌드(Build) / 디렉토리 구조</strong>
앞선 서비스 플로우를 참고했을 때, DB와 자원 처리를 담당하는 Spring이 연결되어 있습니다. DB를 서비스에 연결하기 위해선Java의 jdbc 를 build를 해야합니다.</p>
<p>빌드란 Java 소스코드로부터 실행 가능한 프로그램으로 변환하는 것을 말합니다. 이때 사용하는 도구로 Gradle과 Maven이 있지만 Gradle을 사용하는 것을 권고하고 있습니다. Maven은 언어를 XML을 사용하는데 해당 언어는 의존성이 복잡하기에 Groovy/Kotlin을 기반으로 하는 Gradle을 사용합니다.</p>
<p>Build를 할 때 ‘./gradlew build’ 명령을 사용하게 되는데, 이 때, build.gradle파일이 위치한 곳에서 위의 명령을 입력해야 합니다. 따라서 디렉토리 구조가 중요합니다.</p>
<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/ed9a3f22-b8b7-41de-8f15-73408cd33c49/image.png" alt=""></p>
<p>이제까지 배운 내용을 바탕으로 백엔드 로직을 java를 활용해 간단한 로그인 인증 서비스를 구현해보았습니다. 사용자 인증 정보를 저장할 DB는 MariaDB를 Docker에서 컨테이너로 구동, 사용자 요청 처리를 담당하는 Backend는 pure java, 사용자 입력을 받는 Frontend는 html, css, js를 이용해 Python 명령을 이용해 구동했습니다.</p>
<p>인증을 위해 세션 및 토큰을 더욱 간단한 방법인 spring boot가 아닌 pure 자바를 사용하는 이유는 spring boot는 인증 기능 외에도 프레임워크 차원에서 제공하는 수많은 라이브러리를 함께 로드해야 합니다. 따라서 이러한 무거운 로딩 과정으로 인해 초기 구동 및 요청 처리 시 시간 지연(Latency)이 발생합니다.</p>
<p>Pure 자바를 이용하게 되면 불필요한 외부 라이브러리 없이 필수적인 인증 로직만을 구현할 수 있습니다. 가벼운 구조를 통해 인증 및 인가(Authentication &amp; Authorization) 서비스를 더욱 신속하게 처리하여 전체적인 소요 시간을 단축할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스, Java] 햄버거 만들기]]></title>
            <link>https://velog.io/@l-wanderer01/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Java-%ED%96%84%EB%B2%84%EA%B1%B0-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@l-wanderer01/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Java-%ED%96%84%EB%B2%84%EA%B1%B0-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Sat, 27 Dec 2025 07:37:32 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/a4e2bb6c-7348-4142-96a2-92e05e7719bf/image.jpg" alt=""></p>
<h2 id="1-문제">1. 문제</h2>
<blockquote>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 햄버거 가게에서 일을 하는 상수는 햄버거를 포장하는 일을 합니다. 함께 일을 하는 다른 직원들이 햄버거에 들어갈 재료를 조리해 주면 조리된 순서대로 상수의 앞에 아래서부터 위로 쌓이게 되고, 상수는 순서에 맞게 쌓여서 완성된 햄버거를 따로 옮겨 포장을 하게 됩니다. 상수가 일하는 가게는 정해진 순서(아래서부터, 빵 – 야채 – 고기 - 빵)로 쌓인 햄버거만 포장을 합니다. 상수는 손이 굉장히 빠르기 때문에 상수가 포장하는 동안 속 재료가 추가적으로 들어오는 일은 없으며, 재료의 높이는 무시하여 재료가 높이 쌓여서 일이 힘들어지는 경우는 없습니다.
&nbsp;&nbsp;&nbsp;&nbsp;예를 들어, 상수의 앞에 쌓이는 재료의 순서가 [야채, 빵, 빵, 야채, 고기, 빵, 야채, 고기, 빵]일 때, 상수는 여섯 번째 재료가 쌓였을 때, 세 번째 재료부터 여섯 번째 재료를 이용하여 햄버거를 포장하고, 아홉 번째 재료가 쌓였을 때, 두 번째 재료와 일곱 번째 재료부터 아홉 번째 재료를 이용하여 햄버거를 포장합니다. 즉, 2개의 햄버거를 포장하게 됩니다.
&nbsp;&nbsp;&nbsp;&nbsp;상수에게 전해지는 재료의 정보를 나타내는 정수 배열 ingredient가 주어졌을 때, 상수가 포장하는 햄버거의 개수를 return 하도록 solution 함수를 완성하시오.</p>
</blockquote>
<pre><code>* 제한사항
1. 1 ≤ ingredient의 길이 ≤ 1,000,000
2. ingredient의 원소는 1, 2, 3 중 하나의 값이며, 순서대로 빵, 야채, 고기를 의미합니다.</code></pre><h2 id="2-풀이">2. 풀이</h2>
<pre><code>class Solution {
    public int solution(int[] ingredient) {
        int answer = 0;
        int[] stack = new int[ingredient.length];
        int top = 0; // 가장 상단 index 저장

        for (int i : ingredient) {
            stack[top] = i;

            if (top &gt;= 3 &amp;&amp;
                stack[top - 3] == 1 &amp;&amp;
                stack[top - 2] == 2 &amp;&amp;
                stack[top - 1] == 3 &amp;&amp;
                stack[top] == 1) {

                top -= 4; // 저장됐던 재료 제거
                answer++;

            }

            top++;
        }

        return answer;
    }
}</code></pre><blockquote>
<p><strong>접근 방식</strong></p>
</blockquote>
<ol>
<li>재료의 개수만큼 값을 저장할 수 있는 리스트(stack) 생성</li>
<li>가장 상단에 위치한 재료의 index 저장 (top)</li>
<li>재료를 순차적으로 접근하며 stack에 재료를 저장</li>
<li>stack의 상단에서 3번째 위치부터 상단 위치까지의 재료의 순서 확인 (빵 – 야채 – 고기 - 빵)
&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp 4-1. 순서가 같다면 top의 위치를 옮기며 해당 재료를 제거한 후 햄버거의 개수를 1 증가시킴
&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp 4-2. 순서가 다르다면 다음 재료를 쌓음</li>
<li>상단(top)의 위치를 1 증가시킴</li>
<li>햄버거의 개수를 Return함</li>
</ol>
<h2 id="3-깨달은-점">3. 깨달은 점</h2>
<blockquote>
<p>문제를 봤을 때, 자료구조 선정을 잘 한다면 복잡한 사고 과정없이 문제를 쉽게 접근할 수 있다!
Java 라이브러리를 이용해 Stack을 사용할수도 있지만, List를 활용해 Stack과 같이 사용할수도 있다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스, Java] 체육복]]></title>
            <link>https://velog.io/@l-wanderer01/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Java-%EC%B2%B4%EC%9C%A1%EB%B3%B5</link>
            <guid>https://velog.io/@l-wanderer01/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Java-%EC%B2%B4%EC%9C%A1%EB%B3%B5</guid>
            <pubDate>Tue, 16 Dec 2025 06:00:12 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/c516d3b2-6d97-42a0-afb0-7a4e535144f1/image.jpg" alt=""></p>
<h2 id="1-문제">1. 문제</h2>
<blockquote>
<p>점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다. 학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려줄 수 있습니다. 예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다. 체육복이 없으면 수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 들어야 합니다.
전체 학생의 수 n, 체육복을 도난당한 학생들의 번호가 담긴 배열 lost, 여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때, 체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.</p>
</blockquote>
<pre><code>* 제한사항
1. 전체 학생의 수는 2명 이상 30명 이하입니다.
2. 체육복을 도난당한 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
3. 여벌의 체육복을 가져온 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
4. 여벌 체육복이 있는 학생만 다른 학생에게 체육복을 빌려줄 수 있습니다.
5. 여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다. 이때 이 학생은 체육복을 하나만 도난당했다고 가정하며, 남은 체육복이 하나이기에 다른 학생에게는 체육복을 빌려줄 수 없습니다.</code></pre><h2 id="2-풀이">2. 풀이</h2>
<h3 id="💻-스스로-푼-풀이">💻 스스로 푼 풀이</h3>
<pre><code>1. 체육복을 가진 인원을 저장할 변수 count를 선언함 (전체 인원 수(n) - lost의 길이)
2. lost와 reserve를 정렬함.
3. 접근할 인덱스를 저장할 변수 idx를 선언함.
4. 모든 lost에 대해 reserve에 있는 값들을 순차적으로 탐색함
    3-1. lost[i]의 요소와 reserve[idx]의 요소의 차의 절댓값이 1이하인 경우를 찾음
    3-2. idx와 count를 1 증가 시키고, 반복문을 멈춤
5. 체육복을 가질 수 있는 인원을 return</code></pre><p>위의 방법으로 풀이했을 시, 4개의 TC가 통과되지 않았다.
원인을 생각해보니 제한사항 마지막 부분을 잘못 이해한 것이었다. 여벌 체육복을 가져온 학생이 도난을 당했을 시, 본인이 가져온 여벌 체육복이 우선 선택된다는 것을 간과하여 위의 풀이는 문제를 올바르게 해결하지 못한다.</p>
<h3 id="🔅-hashset을-사용한-풀이">🔅 HashSet을 사용한 풀이</h3>
<pre><code>import java.util.*;

class Solution {
    public int solution(int n, int[] lost, int[] reserve) {
        Set&lt;Integer&gt; lostSet = new HashSet&lt;&gt;();
        Set&lt;Integer&gt; reserveSet = new HashSet&lt;&gt;();

        for (int l : lost) lostSet.add(l);
        for (int r : reserve) reserveSet.add(r);

        // 1단계: 교집합 제거
        Iterator&lt;Integer&gt; it = lostSet.iterator();
        while (it.hasNext()) {
            int s = it.next();
            if (reserveSet.contains(s)) {
                it.remove();
                reserveSet.remove(s);
            }
        }

        int answer = n - lostSet.size();

        // 2단계: 빌려주기
        List&lt;Integer&gt; lostList = new ArrayList&lt;&gt;(lostSet);
        Collections.sort(lostList);

        for (int s : lostList) {
            if (reserveSet.contains(s - 1)) {
                reserveSet.remove(s - 1);
                answer++;
            } else if (reserveSet.contains(s + 1)) {
                reserveSet.remove(s + 1);
                answer++;
            }
        }

        return answer;
    }
}</code></pre><h4 id="새롭게-알게된-것">새롭게 알게된 것</h4>
<blockquote>
<p><strong>HashSet의 메서드</strong>
<code>HashSet.add()</code> : HashSet에 값 추가
<code>HashSet.remove()</code> : HashSet에서 값 제거
<code>Iterator.hasNext()</code> : 가져올 객체가 있으면 true, 없으면 false를 리턴
<code>Iterator.next()</code> : Iterator에서 하나의 객체를 가져옴
<code>HashSet.iterator()</code> : 전체 객체를 대상으로 한 번씩 반복해서 가져오는 반복자</p>
</blockquote>
<ul>
<li>Set에는 순서가 없기 때문에 index로 접근이 불가함. 따라서, Iterator로 변경하여 순서대로 값을 접근하여 비교해야 한다!</li>
</ul>
<h2 id="3-깨달은-점">3. 깨달은 점</h2>
<blockquote>
<p>문제에 대해 처음 접근할 때, 처음에 자료를 저장할 자료구조 선정을 잘한다면 이후 코드를 작성하는데에 있어 원활히 해결되니 자료구조를 잘 선택하자!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스, Java] 완주하지 못한 선수 (자료구조의 중요성)]]></title>
            <link>https://velog.io/@l-wanderer01/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Java-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%9D%98-%EC%A4%91%EC%9A%94%EC%84%B1</link>
            <guid>https://velog.io/@l-wanderer01/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Java-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%9D%98-%EC%A4%91%EC%9A%94%EC%84%B1</guid>
            <pubDate>Sun, 14 Dec 2025 10:30:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/8f3074ce-a573-440f-bbdb-9d00a0266e2c/image.jpg" alt=""></p>
<h2 id="1-문제">1. 문제</h2>
<blockquote>
<p>수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.
마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.</p>
</blockquote>
<pre><code>* 제한사항
마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.
completion의 길이는 participant의 길이보다 1 작습니다.
참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.
참가자 중에는 동명이인이 있을 수 있습니다.</code></pre><h2 id="2-풀이">2. 풀이</h2>
<h3 id="💻-스스로-생각한-풀이">💻 스스로 생각한 풀이</h3>
<pre><code>import java.util.*;

class Solution {
    public String solution(String[] participant, String[] completion) {
        Arrays.sort(participant);
        Arrays.sort(completion);

        for (int i = 0; i &lt; completion.length; i++) {
            if (!participant[i].equals(completion[i])) return participant[i];
        }

        return participant[participant.length - 1];
    }
}</code></pre><br>
문제는 문제없이 통과되었으나, 시간복잡도 측면에서 오래걸리는 문제가 있어 다른 방법으로 풀 수 있는 방법에 대해 찾아보다, HashMap을 사용하면 시간복잡도를 줄일 수 있다는 것을 알게 되었다.
<br>

<h3 id="🔅-hashmap을-사용한-풀이">🔅 HashMap을 사용한 풀이</h3>
<pre><code>import java.util.*;

class Solution {
    public String solution(String[] participant, String[] completion) {
        HashMap&lt;String, Integer&gt; map = new HashMap&lt;&gt;();

        for (String str : participant) {
            map.put(str, map.getOrDefault(str, 0) + 1);
        }

        for (String str : completion) {
            map.put(str, map.get(str) - 1);
        }

        for (String key : map.keySet()) {
            if (map.get(key) &gt; 0) {
                return key;
            }
        }
        return &quot;&quot;;
    }
}</code></pre><h4 id="✅-접근법">✅ 접근법</h4>
<ol>
<li>참가자 전원을 Map에 저장
 <code>map.getOrDefault(key, default)</code> : map에 값을 넣을 때, 값이 없으면 0을 넣음</li>
<li>완주자들을 map에서 찾아 값을 줄임
 <code>map.get(key)</code> : key값의 value를 가져옴</li>
<li>최종 Map에서 값이 1인 Key값을 return!
 <code>map.keySet()</code> : map의 모든 Key 집합을 가져옴</li>
</ol>
<blockquote>
</blockquote>
<p><strong>스스로 푼 방식의 시간복잡도</strong>
: 정렬 + 선형 탐색 = O(nlogn) + O(n) = O(nlogn)</p>
<ul>
<li>정렬에는 비교 연산이 존재</li>
</ul>
<blockquote>
<p><strong>HashMap으로 푼 방식의 시간복잡도</strong>
: 해시 탐색 = O(n)
HashMap의 put / get 연산 : 평균 O(1)</p>
</blockquote>
<ul>
<li>HashMap에서 값을 찾을 때엔 <strong>비교 연산 없이</strong> 해시값 계산 후 바로 접근</li>
</ul>
<p>HashMap은 key를 바로 저장하지 않고, <strong>key → 해시값 → 배열 인덱스</strong>로 변환
➡️ 값을 찾기 위해 처음부터 끝까지 탐색하지 않고, 계산 한 번으로 들어갈 자리를 앎
따라서, 시간복잡도는 평균 O(1), 최악 O(n)이 됨</p>
<h2 id="3-깨달은-점">3. 깨달은 점</h2>
<blockquote>
<p>문제에 대해 접근할 때, 데이터를 어떻게 저장하고 접근할지에 대해 먼저 고민을 하자.
각 자료구조의 특징을 잘 파악해도 시간 복잡도 측면에서 큰 이득을 얻을 수 있다!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stream을 이용한 연산과 for문의 성능 비교]]></title>
            <link>https://velog.io/@l-wanderer01/Stream%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%97%B0%EC%82%B0%EA%B3%BC-for%EB%AC%B8%EC%9D%98-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@l-wanderer01/Stream%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%97%B0%EC%82%B0%EA%B3%BC-for%EB%AC%B8%EC%9D%98-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Mon, 05 May 2025 09:36:31 GMT</pubDate>
            <description><![CDATA[<p>코딩테스트를 풀어가면서 다른 사람들의 풀이를 보다가 나는 for문으로 풀고 다른 사람들은 stream의 forEach문으로 문제를 풀어내는 것을 보고 for문과 forEach의 차이가 궁금해져 알아보게 되었습니다.</p>
<p>for문이 forEach문보다 먼저 개발되었다는 것은 모두가 아는 사실입니다. 그렇다면 for문이 코드를 작성하는데 큰 문제가 없는데 forEach문이 나오게 된 배경을 살펴봅시다.
그렇다면 for문으로도 문제없이 코드를 작성할 수 있는데, 왜 굳이 forEach가 등장하게 되었을까요?</p>
<blockquote>
<p>모든 기능 개발의 원천에는 개발자들의 <strong>귀차니즘</strong>이 존재합니다.</p>
</blockquote>
<p>forEach문도 마찬가지입니다. for문으로 여러 줄을 작성하던 것을 더 간단하고 읽기 쉽게 만든 것이 forEach문입니다.</p>
<p>for문은 아래와 같이 코드가 어쩔 수 없이 길어집니다.</p>
<pre><code>// 배열의 합의 제곱이 배열 요소들의 곱보다 크다면, 1 반환, 아니면 0 반환</code></pre><h3 id="for문-예시">for문 예시</h3>
<pre><code>class Solution {
    public int solution(int[] num_list) {
        int mul = 1;
        int sum = 0;

        for (int i = 0; i &lt; num_list.length; i++) {
            sum += num_list[i];
            mul *= num_list[i];
        }

        return sum * sum &gt; mul ? 1 : 0;
    }
}</code></pre><h3 id="향상된-for문-예시-for-each">향상된 for문 예시 (for-each)</h3>
<pre><code>// 향상된 for문
class Solution {
    public int solution(int[] num_list) {
        int mul = 1;
        int sum = 0;

        for (int i : num_list) {
            sum += i;
            mul *= i;
        }

        return sum * sum &gt; mul ? 1 : 0;
    }
}</code></pre><p>🌟 <strong>tip)</strong> 향상된 for문을 사용하게 되면 인덱스를 사용하지 않아도 되기에 <code>IndexOutOfBoundsException</code>이 발생하지 않아 코드의 <strong>안정성</strong>이 올라갑니다.</p>
<p>향상된 for문을 활용하면 코드가 조금은 깔끔해진 느낌이 든다. 그러나 이것보다 간략히 코드를 작성할 수 있다면?</p>
<p>Stream을 활용하면 향상된 for문보다 더 짧은 코드를 제공합니다.</p>
<h3 id="stream-api를-사용한-코드">stream API를 사용한 코드</h3>
<pre><code>class Solution {
    public int solution(int[] num_list) {
        int mul = 1;
        int sum = 0;

        sum = Arrays.stream(num_list).sum();
        mul = Arrays.stream(num_list).reduce(1, (i, j) -&gt; i * j);

        return sum * sum &gt; mul ? 1 : 0;
    }
}</code></pre><p>위의 코드와 같이 코드의 길이가 많이 줄어듭니다. 따라서 가독성이 향상됩니다! stream API는 Java 8부터 도입된 기능으로, 데이터를 함수형 스타일로 처리할 수 있게 도와줍니다. 내부적으로 반복문을 추상화한 것이며, 일반적으로 for문보단 약간 느리지만, 차이는 대부분의 경우 미미하며 코드의 간결성과 함수형 스타일이 장점으로 언급됩니다.</p>
<ul>
<li>다만 간단한 집계 연산이나 변환에는 유용하지만 stream은 연산 중간에 <code>break</code>, <code>continue</code>를 사용하기 까다롭다는 점이 있고, stream을 사용하면 경우에 따라 <code>map/filter</code> 등으로 복잡해지는 경우 오히려 가독성을 해칠 수 있습니다.</li>
<li>또한 stream은 for문보다 약간의 오버헤드가 있을 수 있으므로, 성능이 민감한 경우에는 주의가 필요합니다.</li>
</ul>
<p>그렇다면 stream이 좋은가? for문이 좋은가? 이 문제는 상황에 따라 다릅니다. stream을 사용한다고 해서 항상 가독성이 뛰어난 것도 아니고, 항상 for문을 사용하기에는 귀찮음이 동반될 수 있습니다.</p>
<blockquote>
<p>따라서 반드시 for문 혹은 stream을 사용하는 것이 아닌 상황에 맞게 올바른 문법을 채택하는게 권장됩니다.
➡️ <strong>가독성, 성능, 제어 흐름의 유연성 등을 고려</strong></p>
</blockquote>
<p>💡 개발에서 중요한 건 “지금 이 문법이 이 상황에 가장 적절한가?&quot;를 판단하는 것!</p>
<p><code>이미지 출처</code> : [Baeldung 블로그] (<a href="https://velog.io/@haleyjun/%EB%B0%98%EB%B3%B5%EB%AC%B8%EC%9D%98-%EC%A2%85%EB%A5%98">https://velog.io/@haleyjun/%EB%B0%98%EB%B3%B5%EB%AC%B8%EC%9D%98-%EC%A2%85%EB%A5%98</a>)
<code>참고 출처</code> : [랸나 [JAVA] For과 Stream은 어떤 차이가 있는걸까?] (<a href="https://velog.io/@foureaf/JAVA-For%EA%B3%BC-Stream%EC%9D%80-%EC%96%B4%EB%96%A4-%EC%B0%A8%EC%9D%B4%EA%B0%80-%EC%9E%88%EB%8A%94%EA%B1%B8%EA%B9%8C">https://velog.io/@foureaf/JAVA-For%EA%B3%BC-Stream%EC%9D%80-%EC%96%B4%EB%96%A4-%EC%B0%A8%EC%9D%B4%EA%B0%80-%EC%9E%88%EB%8A%94%EA%B1%B8%EA%B9%8C</a>)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Docker란?]]></title>
            <link>https://velog.io/@l-wanderer01/Docker%EB%9E%80</link>
            <guid>https://velog.io/@l-wanderer01/Docker%EB%9E%80</guid>
            <pubDate>Sun, 04 May 2025 11:44:23 GMT</pubDate>
            <description><![CDATA[<h1 id="docker란-무엇인가">Docker란 무엇인가?</h1>
<blockquote>
<p>도커란 컨테이너를 활용해 분리된 환경에서 프로그램이 동작할 수 있도록 돕는 프로그램</p>
</blockquote>
<p>도커의 장점, 도커를 사용하는 이유! <strong>이식성</strong></p>
<p>이식성이란? 어떤 프로그램을 다른 환경에서 쉽게 설치 및 실행이 가능한 것을 의미한다.</p>
<p>즉, 명령어 한 줄로 어떤 환경에서든 프로그램(e.g, MySQL)을 문제 없이 설치 및 실행이 가능하다.</p>
<h1 id="docker에서의-중요한-개념">Docker에서의 중요한 개념</h1>
<h2 id="container">Container</h2>
<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/9e9f8570-4672-41a2-9e98-720a2fe7fd1d/image.png" alt=""></p>
<blockquote>
<p>컨테이너란? 쉽게 말해 미니 컴퓨터이다.</p>
</blockquote>
<p>컨테이너 별로 서로 다른 환경을 제공한다.
(e.g., 한 컨테이너에서 카카오톡을 설치했다고 해서, 다른 컨테이너에서도 카카오톡이 설치되지는 않는다. 다른 컨테이너에 카카오톡을 설치해야만 해당 컨테이너에서 카카오톡이 실행 가능하다.)
➡️ 즉 독립성을 제공한다.</p>
<p><strong>독립성</strong> : 분리해 서로가 영향을 끼지치 않는 것
이때 저장 공간과 네트워크를 컨테이너마다 분리한다.</p>
<h2 id="image">Image</h2>
<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/bdbacc47-1c21-4966-a798-7ceaf12f5e82/image.png" alt=""></p>
<blockquote>
<p>이미지란? 닌텐도의 게임칩과 같다.</p>
</blockquote>
<p>프로그램 설치를 위해 필요한 설정, 버전 정보 등을 포함하고 있다.</p>
<p>예를 들어, MySQL 이미지를 다운받고 실행했을 때, 해당 이미지가 컨테이너를 통해 실행이 되고, 별도의 설치 없이 MySQL을 사용할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 기초]]></title>
            <link>https://velog.io/@l-wanderer01/Spring-Boot-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@l-wanderer01/Spring-Boot-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Fri, 21 Mar 2025 12:47:30 GMT</pubDate>
            <description><![CDATA[<h3 id="핵심">핵심</h3>
<p>1️⃣ Spring과 Spring Boot의 차이!</p>
<p>2️⃣ MVC 패턴 개념!</p>
<p>3️⃣ DTO를 활용해 데이터를 다룸</p>
<h1 id="1-spring과-spring-boot의-차이">1. Spring과 Spring Boot의 차이</h1>
<p>참고) 둘 다 Java 기반 웹 애플리케이션 프레임워크</p>
<table>
<thead>
<tr>
<th>비교 항목</th>
<th>Spring Framework</th>
<th>Spring Boot</th>
</tr>
</thead>
<tbody><tr>
<td>설정 방식</td>
<td>XML, Java Config 필요</td>
<td>자동 설정 (<code>@SpringBootApplication</code>)</td>
</tr>
<tr>
<td>웹 서버</td>
<td>수동 설정 필요</td>
<td>내장 Tomcat 자동 제공</td>
</tr>
<tr>
<td>의존성 관리</td>
<td>수동 설정 필요</td>
<td><code>spring-boot-starter-*</code> 제공</td>
</tr>
<tr>
<td>프로젝트 구조</td>
<td>복잡한 설정 필요</td>
<td>간단한 구조</td>
</tr>
<tr>
<td>목적</td>
<td>확장성과 유연성 중점</td>
<td>빠른 개발과 설정 최소화</td>
</tr>
</tbody></table>
<blockquote>
<p>🔥 <strong>Spring Boot는 Spring을 더 쉽게 사용할 수 있도록 만든 버전</strong></p>
</blockquote>
<p>참고) <strong>Spring 프레임워크</strong> : Servlet과 JSP의 복잡한 설정, 코드 중복, 유지보수 어려움 등의 문제로 탄생</p>
<h3 id="1-1-spring의-핵심-기능">1-1. Spring의 핵심 기능</h3>
<p>→ OOP의 원칙을 따르며 객체 간의 의존성을 줄이고, 유지보수를 쉽게 할 수 있도록 도움</p>
<ul>
<li><strong>OOP solid</strong>
  → <strong>객체지향 설계</strong>에서 지켜줘야할 5개의 <strong>소프트웨어 개발 원칙</strong>!<br><blockquote>
<p>   <strong>S</strong>RP(Single Responsibility Principle) : 단일 책임 원칙<br>
   <strong>O</strong>CP(Open Closed Principle) : 개방 폐쇄 원칙<br><br>   <strong>L</strong>SP(Liskov Substitution Principle) : 리스코프 치환 원칙<br>
   <strong>I</strong>SP(Interface Segregation Principle) : 인터페이스 분리 원칙<br>
   <strong>D</strong>IP(Dependency Inversion Principle) : 의존 역전 원칙<br></p>
</blockquote>
</li>
</ul>
<p> ⇒ 여러 디자인 패턴들이 SOLID 설계 원칙에 입각해 만들어짐</p>
<ol>
<li><p>DI(의존성 주입)
 → 객체가 직접 다른 객체를 생성하지 않고, 외부에서 주입받는 방식(Spring에서는 의존성을 자동으로 주입해줌)
 ➡️ 객체 간의 결합도를 낮추고 유지보수를 쉽게 만듦</p>
<blockquote>
<p><code>new</code> 키워드로 직접 객체 생성 시 의존성이 발생 → <code>생성자</code>를 이용해 의존성을 주입</p>
</blockquote>
<pre><code class="language-java">   class B {
       public void print() {
           System.out.println(&quot;B 클래스의 메서드 실행&quot;);
       }
   }

   class A {
       private B b;

       public A(B b) { // 생성자를 이용해 B 객체를 주입
           this.b = b;
       }

       public void execute() {
           b.print();
       }
   }</code></pre>
<blockquote>
<ul>
<li>A 클래스는 B를 직접 생성하지 않는다!</li>
<li>B의 구현이 바뀌더라도 A는 영향을 받지 않음!</li>
</ul>
</blockquote>
</li>
<li><p>AOP(관점 지향 프로그래밍)</p>
<p> → 공통적으로 적용해야 하는 기능을 분리해 코드 중복을 줄이고 유지보수를 쉽게하는 프로그래밍 기법</p>
<p> ➡️ 중복된 코드를 하나의 공통 기능으로 분리해 유지보수성을 높이기 위해 <strong>AOP</strong> 사용!</p>
</li>
<li><p>Spring MVC</p>
<p> → 웹 애플리케이션에서 <code>Model-View-Control</code>  구조를 지원</p>
</li>
<li><p>Spring Security</p>
<p> → Spring 기반 애플리케이션의 보안을 담당하는 프레임워크</p>
<p> ➡️ 로그인, 로그아웃, 권한 부여, 요청 보호 등의 기능을 쉽게 구현할 수 있도록 도와줌</p>
<p> 참고) Django의 <code>django.contrib.auth</code> 가 제공하는 기능과 비슷한 역할</p>
</li>
<li><p>Spring Data JPA</p>
<p> → Spring에서 JPA(Java Persistence API)를 더 쉽게 사용할 수 있도록 도와주는 라이브러리</p>
<p> ➡️ SQL을 직접 작성하지 않아도 자동으로 데이터베이스와 연결하고, CRUD를 수행 가능!</p>
</li>
</ol>
<h3 id="1-2-spring의-단점">1-2. Spring의 단점</h3>
<ol>
<li><p>초기 설정(XML 설정)이 복잡</p>
</li>
<li><p>초기 학습 난이도 ⬆️</p>
<ul>
<li>DI, AOP, MVC 등 많은 개념이 있어 처음 배우는 사람이 이해하기 어려움</li>
</ul>
</li>
<li><p>초기 부팅이 오래 걸림</p>
<ul>
<li><p>Spring은 애플리케이션이 시작될 때 모든 객체(Bean)를 미리 생성하고 설정하는 과정이 존재</p>
<p>⇒ Java 프로젝트보다 부팅 속도가 느릴 수 있음</p>
</li>
</ul>
</li>
</ol>
<h1 id="2-spring-boot">2. Spring Boot</h1>
<h3 id="2-1-spring-boot">2-1. Spring Boot</h3>
<blockquote>
<p>🔨    Spring 프레임워크를 더 쉽고 빠르게 사용할 수 있도록 도와주는 도구</p>
</blockquote>
<p>→ Spring의 복잡한 설정을 최소화</p>
<h3 id="2-2-spring-boot의-특징">2-2 Spring Boot의 특징</h3>
<ol>
<li><p>자동 설정</p>
<p> ___Application.java: <code>@SpringBootApplication</code> 한 줄로 설정 가능</p>
<p> build.gradle: <code>spring-boot-starter-*</code>  의존성을 추가하면 자동으로 관련 설정이 적용됨</p>
</li>
<li><p>내장 웹 서버 제공(Built-in Web Server)</p>
<ul>
<li>웹 서버를 내장하고 있어 따로 설정하지 않아도 됨
➡️ Spring Boot는 실행하면 자동으로 서버가 실행됨</li>
</ul>
</li>
<li><p>의존성 자동 관리</p>
<p> build.gradle: <code>spring-boot-starter-*</code> : 의존성 관리를 쉽게 할 수 있도록 패키지를 제공</p>
</li>
<li><p>독립 실행 가능</p>
<p> <strong>JAR 파일</strong>로 패키징하면 실행 파일처럼 동작할 수 있음</p>
<p> → AWS 서버에 올려 실행시킬 때, JAR 파일로 패키징해 서버를 실행!</p>
</li>
<li><p>환경설정의 유연함</p>
<p> <code>application.properties</code> 또는 <code>application.yml</code> 을 사용해 설정 가능</p>
</li>
</ol>
<h3 id="2-3-spring-boot의-주요-기능">2-3. Spring Boot의 주요 기능</h3>
<ol>
<li><p><strong>내장 웹 서버</strong> 지원</p>
<p> Tomcat, Jetty, Undertow와 같은 웹 서버를 내장하고 있어 따로 설정할 필요 x</p>
</li>
<li><p><strong>REST API</strong> 개발(Spring MVC)</p>
<p> 간단한 컨트롤러만 만들어도 API가 동작</p>
</li>
<li><p>Spring Boot + <strong>JPA</strong> 사용(DB 연동)</p>
<p> Spring Data JPA와 함께 사용하면 데이터베이스 연동이 간단함</p>
</li>
</ol>
<h3 id="spring-boot-실습">Spring Boot 실습</h3>
<ol>
<li><p><strong>Spring Initialzr 접속</strong></p>
<p> <a href="https://start.spring.io/">https://start.spring.io/</a></p>
</li>
<li><p><strong>Project Type 선택</strong></p>
<p> Java 프로젝트에서는 라이브러리(의존성) 관리, 빌드, 테스트, 배포를 자동화 하는 도구가 필요</p>
<p> <strong>⇒ Maven, Gradle</strong></p>
<ul>
<li><p><strong>Maven</strong></p>
<ul>
<li><p>XML(pom.xml) 기반의 전통적인 빌드 도구</p>
</li>
<li><p><code>pom.xml</code> 을 사용해 프로젝트를 빌드하고, 의존성을 관리
<img src="https://velog.velcdn.com/images/l-wanderer01/post/7156e536-8e18-45f2-9901-31f7e135c8b8/image.png" alt=""></p>
<p>XML 형식 : 가독성이 낮고, 길어질수록 설정이 복잡</p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<pre><code>- **Gradle**
    - `build.gradle` 을 사용해 Maven에 비해 더 빠르고 유연함!
    - Gradle이 속도 면에서 Maven보다 빠름
    ![](https://velog.velcdn.com/images/l-wanderer01/post/e1fb7f9e-d20b-41d0-82a6-5233d0ab707d/image.png)


    Maven에 비해 작관적이고, 읽기 쉬움</code></pre><ol start="3">
<li><strong>Spring Boot Version 선택</strong></li>
</ol>
<ul>
<li>정식 버전을 주로 사용</li>
<li>SNAPSHOT 버전: 개발 중인 미완성 버전, 불안정</li>
<li>M 버전(Milestone Release) : 정식 릴리즈 전 중요한 기능을 테스트하는 중간 단계(베타 버전과 유사, But 정식 출시가 아니므로 주의가 필요)</li>
</ul>
<ol start="4">
<li><strong>Project Metadata 입력</strong></li>
</ol>
<ul>
<li>Group: 패키지의 그룹 ID ➡️ 회사, 조직, 프로젝트 단위를 나타냄</li>
<li>Artifact: 프로젝트의 고유한 이름(JAR 파일의 기본 이름이 됨)</li>
<li>Name: 프로젝트의 기본 이름(기본적으로 Artifact와 동일하게 설정됨)</li>
<li>Description: 프로젝트에 대한 간단한 설명
<code>pom.xml</code> or <code>build.gradle</code> 파일에 포함</li>
<li>Package name: 프로젝트의 기본 패키지 이름(Group + Artifact 조합)</li>
<li>Packaging: 프로젝트를 빌드할 때 생성되는 최종 실행 파일의 형식을 결정<ul>
<li>Jar(Java Archive) - 하나의 독립 실행 가능한 <code>.jar</code> 파일 생성</li>
<li>War(Web Application Archive) - 기존의 애플리케이션 서버에 <code>.war</code> 파일을 생성해 배포 가능</li>
</ul>
</li>
</ul>
<ol start="5">
<li><strong>Dependencies(의존성) 주입</strong></li>
</ol>
<ul>
<li>프로젝트가 정상적으로 실행되기 위해 필요한 라이브러리들 모음
Maven or Gradle을 사용해 필요한 라이브러리를 자동으로 다운로드하고 관리할 수 있도록 도움</li>
</ul>
<p>🌟 <strong>Spring Boot 웹 프로젝트 Dependencies</strong></p>
<ol>
<li>Spring Web</li>
</ol>
<p>가장 기본적인 의존성, 내장 Tomcat 서버와 함께 RESTful API를 쉽게 만들 수 있도록 지원</p>
<p>➡️ 컨트롤러를 만들어 HTTP 요청 처리 가능</p>
<hr>
<ol start="2">
<li>Lombok                </li>
</ol>
<p><code>Getter</code>, <code>Setter</code>, <code>생성자</code>, <code>toString</code> 등의 메서드를 자동으로 생성해주는 라이브러리</p>
<p>➡️ DTO와 Entity 클래스를 만들 때 불필요한 코드 작성을 줄임</p>
<hr>
<ol start="3">
<li>Spring Boot Validation</li>
</ol>
<p>사용자 입력값을 검증하는 라이브러리(잘못된 입력 시 오류 반환)</p>
<p>➡️ <code>@Valid</code> or <code>@NotNull</code> 등을 사용해 REST API의 요청 데이터를 검증 가능</p>
<hr>
<ol start="4">
<li>Spring Data JPA</li>
</ol>
<p>MySQL과 같은 관계형 데이터베이스를 쉽게 다룰 수 있도록 JPA 지원</p>
<hr>
<ol start="5">
<li>MySQL JDBC Driver</li>
</ol>
<p>Spring Boot에서 MySQL과 연동하기 위해 필요한 JDBC 드라이버</p>
<p>💡 본인이 사용하는 DB에 맞는 JDBC Driver 추가</p>
<hr>
<h1 id="3-mvc-패턴">3. MVC 패턴</h1>
<blockquote>
<p>🔥 애플리케이션을 <strong>Model, View, Controller</strong>로 나누어 구성하는 방식
→ 유지보수성과 확장성 ⬆️, 코드의 역할을 명확히 구분❗️</p>
</blockquote>
<h3 id="3-1-mvc-패턴의-구성요소">3-1. MVC 패턴의 구성요소</h3>
<ul>
<li><p><strong>Model</strong></p>
<p>  📌 애플리케이션의 핵심 <strong>데이터</strong>와 <strong>비즈니스 로직</strong>을 처리</p>
<ul>
<li>구성요소</li>
</ul>
<ol>
<li><p>Entity : 데이터베이스 테이블과 연결되는 객체 (파일명: <code>User.java</code>)</p>
<p> → Getter, Setter 필수</p>
</li>
<li><p>Repository : 데이터 엑세스를 담당하는 계층 (파일명: <code>UserRepository.java</code>)</p>
</li>
<li><p>Service : 비즈니스 로직을 처리하는 계층 (파일명: <code>UserService.java</code>)</p>
</li>
</ol>
</li>
<li><p><strong>Controller</strong></p>
<p>  📌 사용자의 요청을 받아 Model(Service)과 View(JSON, HTML)사이에서 <strong>데이터를 주고받는 역할</strong></p>
<p>  <strong>Model</strong>과 <strong>View</strong>의 중간다리 역할!</p>
<p>  👉 <strong>사용자의 요청을 받고 응답을 반환하는 역할</strong></p>
<p>  👉 <strong>비즈니스 로직을 직접 수행하지 않고, Service에 요청을 전달</strong></p>
<ul>
<li>Controller와 Service
<img src="https://velog.velcdn.com/images/l-wanderer01/post/8871c94a-80a7-46b7-9407-4ba1162d7f29/image.png" alt=""></li>
</ul>
</li>
</ul>
<blockquote>
<p><code>Controller</code> 는 <strong>HTTP 요청 처리</strong>
   <code>Service</code> 는 실제 <strong>비즈니스 로직 담당</strong></p>
</blockquote>
<ul>
<li><p><strong>View</strong></p>
<p>  📌 사용자가 볼 수 있는 <strong>화면</strong> 또는 <strong>응답 데이터</strong>(JSON)</p>
<ul>
<li>Thymeleaf 또는 JSON 응답 방식으로 구현<ul>
<li>Thymeleaf: HTML 기반 View → 프론트엔드가 없는 프로젝트에서 사용</li>
<li>JSON 응답: 프론트엔드와 통신할 때 사용</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>├── src
│   ├── main
│   │   ├── java
│   │   │   └── com.companyname.projectname
│   │   │       ├── post
│   │   │       │   ├── controller
│   │   │       │   │   └── PostController.java
│   │   │       │   ├── dto
│   │   │       │   │   ├── PostRequestDto.java
│   │   │       │   │   ├── PostResponseDto.java
│   │   │       │   │   ├── PostListResponseDto.java
│   │   │       │   │   ├── PostSaveRequestDto.java
│   │   │       │   │   └── PostUpdateRequestDto.java
│   │   │       │   ├── model
│   │   │       │   │   └── Post.java
│   │   │       │   ├── repository
│   │   │       │   │   └── PostRepository.java
│   │   │       │   └── service
│   │   │       │       └── PostService.java
│   │   │       └── ProjectNameApplication.java
│   │   └── resources
│   └── test</code></pre><h3 id="3-2-spring-boot에서-mvc-흐름">3-2. Spring Boot에서 MVC 흐름</h3>
<p>   <img src="https://velog.velcdn.com/images/l-wanderer01/post/0317408d-ba7d-4f5a-abaa-6248dda4c8b2/image.png" alt=""></p>
<ol>
<li><p><strong>Client</strong>가 GET 요청을 보냄</p>
</li>
<li><p><strong>Controller</strong>가 요청을 받아 <strong>Service</strong> 호출</p>
</li>
<li><p><strong>Service</strong>가 <strong>Repository</strong>를 통해 데이터베이스에서 데이터를 조회</p>
<ul>
<li>Service
  👉 <strong>비즈니스 로직을 담당하는 계층</strong>
  👉 <strong>Controller에서 받은 데이터를 가공하거나 데이터베이스에 저장</strong></li>
</ul>
</li>
<li><p>조회된 데이터를 <strong>Controller</strong>로 반환</p>
</li>
<li><p><strong>Controller</strong>가 JSON 데이터를 <strong>Client</strong>에게 응답</p>
</li>
</ol>
<h3 id="3-3-mvc-패턴-실습">3-3. MVC 패턴 실습</h3>
<ol>
<li><p>데이터 모델(DTO) 구축</p>
</li>
<li><p>데이터 저장소(Service) 구축</p>
<p> → 회원 데이터를 모델에 저장하는 서비스 클래스 생성</p>
</li>
<li><p>Controller 생성(JSON 응답 반환)</p>
<p> <code>@RestController</code> : HTML 대신 JSON 응답 반환</p>
<p> <code>@GetMapping</code> : 모든 회원 데이터를 JSON으로 반환</p>
<p> <code>@PostMapping</code> + <code>@RequestBody</code> : JSON 데이터를 받아 처리</p>
</li>
</ol>
<h1 id="4-dto-개념-및-활용">4. DTO 개념 및 활용</h1>
<h3 id="4-1-dto의-개념">4-1. DTO의 개념</h3>
<blockquote>
<p>🔥 <strong>Data Transfer Object</strong>
<strong>데이터를 객체로 변환</strong>하여 전송하는 객체
주로 <strong>Controller</strong> ↔ <strong>Service</strong> ↔ <strong>Repository</strong> 사이에서 데이터를 주고받을 때 사용</p>
</blockquote>
<ul>
<li>Entity를 직접 사용하지 않고, DTO를 통해 데이터를 가공 후 API 응답으로 변환
→ DTO는 Entity를 보호하면서 API에 <strong>필요한 데이터만 전달</strong>할 수 있도록 설계됨
매우 민감한 password 와 같은 정보를 선택적으로 주고받을 수 있음!</li>
</ul>
<h3 id="4-2-dto를-사용하는-이유">4-2 DTO를 사용하는 이유</h3>
<ol>
<li>보안 강화</li>
<li>Entity와 요청/응답 데이터를 분리해 유지보수성 ⬆️</li>
<li>여러 개의 Entity 데이터를 조합해 응답 가능(데이터 가공)</li>
<li>쉬운 유효성 검사( <code>@Valid</code> )</li>
<li>불필요한 데이터 노출 방지</li>
</ol>
<h3 id="4-3-dto-vs-entity">4-3. DTO vs Entity</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>DTO</th>
<th>Entity</th>
</tr>
</thead>
<tbody><tr>
<td>역할</td>
<td>데이터 <strong>전달</strong> 전용</td>
<td><strong>데이터베이스</strong>와 직접 연결</td>
</tr>
<tr>
<td>목적</td>
<td>Controller ↔ Service ↔ Repository 사이에서 <strong>데이터 교환</strong></td>
<td>데이터베이스 테이블과 <strong>매핑</strong></td>
</tr>
<tr>
<td>비즈니스 로직 포함 여부</td>
<td>X (데이터만 포함)</td>
<td>O (비즈니스 로직 포함 가능)</td>
</tr>
<tr>
<td>DB 테이블과 연결 여부</td>
<td>X</td>
<td>O (JPA <code>@Entity</code> 사용)</td>
</tr>
<tr>
<td>보안성</td>
<td>필요 없는 데이터 필터링 가능</td>
<td>모든 데이터가 노출될 가능성 존재</td>
</tr>
<tr>
<td>유지보수성</td>
<td>DTO만 변경 가능(Entity와 <strong>독립적인</strong> 관계)</td>
<td>Entity 변경 시 <strong>API 전체 수정</strong> 필요</td>
</tr>
<tr>
<td>사용 위치</td>
<td>API 요청/응답, <strong>데이터 가공</strong>용</td>
<td><strong>데이터 저장 및 조회</strong>용</td>
</tr>
<tr>
<td>사용 방식</td>
<td>JSON ↔ DTO 변환</td>
<td>Entity ↔ DTO 변환 후 사용</td>
</tr>
</tbody></table>
<p>→ <strong>Entity</strong>: 데이터베이스에 저장될 데이터 구조</p>
<p>→ <strong>DTO</strong>: Entity에서 필요한 데이터만 <strong>“선별”</strong>하여 클라이언트에 저장</p>
<p><strong>🚨 Entity를 직접 사용 X</strong></p>
<ul>
<li>Entity 리스트</li>
</ul>
<pre><code class="language-java">[
    User{id=1, username=&quot;Alice&quot;, email=&quot;alice@example.com&quot;},
    User{id=2, username=&quot;Bob&quot;, email=&quot;bob@example.com&quot;}
]</code></pre>
<ul>
<li>DTO 사용 후 변환된 리스트</li>
</ul>
<pre><code class="language-java">[
    UserResponseDto{username=&quot;Alice&quot;, email=&quot;alice@example.com&quot;},
    UserResponseDto{username=&quot;Bob&quot;, email=&quot;bob@example.com&quot;}
]</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[OSI 모델과 TCP/IP 프로토콜]]></title>
            <link>https://velog.io/@l-wanderer01/OSI-%EB%AA%A8%EB%8D%B8%EA%B3%BC-TCPIP-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C</link>
            <guid>https://velog.io/@l-wanderer01/OSI-%EB%AA%A8%EB%8D%B8%EA%B3%BC-TCPIP-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C</guid>
            <pubDate>Tue, 18 Mar 2025 12:54:30 GMT</pubDate>
            <description><![CDATA[<h1 id="1-프로토콜-계층구조-protocol-layer">1. 프로토콜 계층구조 (Protocol Layer)</h1>
<h3 id="프로토콜이란">프로토콜이란?</h3>
<blockquote>
<p>컴퓨터 또는 전자 기기 간의 원활한 통신을 위해 지키기로 한 규약</p>
</blockquote>
<ul>
<li>두 개체 간의 통신을 할 때 필요
📌 개체란? 통신에 참여하는 기계
<img src="https://velog.velcdn.com/images/l-wanderer01/post/5d6cf34d-f553-4c44-a81c-715bbe4b7396/image.png" alt="">
Maria는 <code>스페인어</code>만 하고, Ann은 <code>영어</code>만 한다고 가정을 했을 때, 둘 다 <code>수화</code>를 할 수 있다면, 이때 프로토콜은 <code>수화</code></li>
<li><strong>통신</strong>은 여러 계층 구조로 분할이 가능한데, 이때 각 계층은 <em>각각의 프로토콜 </em>이 필요함!<h3 id="💡-떨어진-환경에서-서로-다른-언어를-사용하는-두-사람이-통신하는-과정">💡 떨어진 환경에서 서로 다른 언어를 사용하는 두 사람이 통신하는 과정</h3>
<img src="https://velog.velcdn.com/images/l-wanderer01/post/3af97183-51db-49ab-bca4-4c0eb4d13f78/image.png" alt=""></li>
</ul>
<h1 id="2-osi-모델-model">2. OSI 모델 (Model)</h1>
<blockquote>
<p>국제표준화기구(ISO) 표준이 <strong>OSI</strong> 모델</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/dcfe91f2-393e-494b-8369-515119e5b212/image.png" alt=""></p>
<table>
<thead>
<tr>
<th align="center"></th>
<th align="center">Layer명</th>
<th align="center">역할</th>
<th align="center">Device</th>
<th align="center">예시</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>1계층</strong></td>
<td align="center">물리 계층</td>
<td align="center">0과 1을 아날로그로 변환</td>
<td align="center">허브</td>
<td align="center">도로, 택배 트럭, <br>택배 상자</td>
</tr>
<tr>
<td align="center"><strong>2계층</strong></td>
<td align="center">데이터 링크</td>
<td align="center">hop-to-hop delivery를 위해 <br>bit를 프레임으로 묶음</td>
<td align="center">스위치, 브릿지</td>
<td align="center">상자에 붙이는  송장</br>(MAC 주소)</td>
</tr>
<tr>
<td align="center"><strong>3계층</strong></td>
<td align="center">네트워크 계층</td>
<td align="center">전달되는 길을 찾음</td>
<td align="center">라우터</td>
<td align="center">주소와 우편번호를 보고 길을 찾음<br>(IP 주소, 라우팅)</td>
</tr>
<tr>
<td align="center"><strong>4계층</strong></td>
<td align="center">전송 계층</td>
<td align="center">전송 시 사고가 나도 <br>수습이 가능하도록 만듦</td>
<td align="center">포트 번호</td>
<td align="center">택배를 나눠 보내거나 묶어서 보내기<br>(TCP/UDP)</td>
</tr>
<tr>
<td align="center"><strong>5계층</strong></td>
<td align="center">세션 계층</td>
<td align="center">통신 세션을 설정, 유지, 종료</td>
<td align="center">로그인 유지, 세션 유지</td>
<td align="center">고객과 택배 회사의 연락<br>(세션 유지)</td>
</tr>
<tr>
<td align="center"><strong>6계층</strong></td>
<td align="center">표현 계층</td>
<td align="center">번역, 암호화, 압축</td>
<td align="center">TLS/SSL(HTTPS 보안), <br>파일 인코딩 변환</td>
<td align="center">주소를 한글에서 영어로 변환<br>(데이터 변환, 암호화)</td>
</tr>
<tr>
<td align="center"><strong>7계층</strong></td>
<td align="center">응용 계층</td>
<td align="center">아래 계층에 요구를 전달</td>
<td align="center">웹 브라우저, 이메일, FTP 서버</td>
<td align="center">고객이 택배를 주문하고 받음<br>(웹 브라우저, 이메일)</td>
</tr>
<tr>
<td align="center">참고)</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">- 네트워크 계층은 해당 길이 올바른 길인지는 모름, Dijkstra 알고리즘 사용</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">- 트랜잭션: 일련의 과정, 과정 중 <code>error</code>가 발생했다면 정상적인 상태로 &quot;<strong>Rollback</strong>&quot; 가능해야 함.</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<blockquote>
<p>1계층부터 4계층까지는 네트워크, 5계층부터 7계층은 코딩으로 구현해야한다!
<br>통신은 <strong>계층-대-계층(Layer-to-Layer)</strong>으로 이루어지며 해당되지 않는 계층의 내용은 보지 못한다!</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/a1c7e164-b92a-40c5-aecd-27c8a3a989d8/image.png" alt="">
➡️ 정보를 보낼 때, Layer를 지날 때마다 Header를 붙이고 Target에 도착하면 Header를 제거하며 Layer를 통과(<em>계층마다 프로토콜이 다름</em> )</p>
<h1 id="3-tcpip-프로토콜-그룹protocol-suite">3. TCP/IP 프로토콜 그룹(Protocol Suite)</h1>
<blockquote>
<p>TCP/IP 프로토콜 그룹은 OSI 모델보다 먼저 개발됨</p>
</blockquote>
<ul>
<li>TCP/IP 프로토콜의 계층은 OSI 모델의 계층과 정확하게 일치하지 않는다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/5dfd5d9e-9196-4358-961c-c902c427cefc/image.png" alt=""></p>
<table>
<thead>
<tr>
<th align="center">TCP/IP 프로토콜</th>
<th align="center">Layer명</th>
<th align="center">통신 단위</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>1계층</strong></td>
<td align="center">물리 계층</td>
<td align="center">Bit</td>
</tr>
<tr>
<td align="center"><strong>2계층</strong></td>
<td align="center">데이터 링크 계층</td>
<td align="center">Frame</td>
</tr>
<tr>
<td align="center"><strong>3계층</strong></td>
<td align="center">네트워크 계층</td>
<td align="center">Datagram</td>
</tr>
<tr>
<td align="center"><strong>4계층</strong></td>
<td align="center">전송 계층</td>
<td align="center">Segment, User Datagram</td>
</tr>
<tr>
<td align="center"><strong>5계층</strong></td>
<td align="center">응용 계층</td>
<td align="center">Message</td>
</tr>
<tr>
<td align="center">참고) 전송 계층(4 Layer): 신뢰성을 높여줌 (reliable)</td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<h1 id="4-주소지정-addressing">4. 주소지정 (Addressing)</h1>
<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/d17308f2-006e-4fa6-97ce-4c90eac7b28c/image.png" alt=""></p>
<blockquote>
<p>TCP/IP 프로토콜을 이용한 인터넷은 4개의 서로 다른 계층의 주소가 사용됨
<br>1. 물리 주소 (Physical address) ➡️ 2계층
<br>2. 논리 주소 (Logical address) ➡️ 3계층
<br>3. 포트 주소 (Port address) ➡️ 4계층
<br>4. 응용-특수 주소 (Application-specific Address) ➡️ 5계층</p>
</blockquote>
<h2 id="41-물리-주소">4.1 물리 주소</h2>
<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/ee70daa2-22ed-4bc9-8bb3-7777b275f340/image.png" alt=""></p>
<ul>
<li><p>두 node는 <strong>링크(link)</strong>로 연결되어있음(LAN에 연결)
프레임(Frame)은 헤더 내에 물리 주소를 포함, 수신된 데이터는 프레임에 <strong>캡슐화</strong>됨</p>
</li>
<li><p>프레임은 <strong>LAN</strong>을 통해 전달</p>
</li>
<li><p>헤더 안의 목적지 주소가 본인이 아니라면 해당 컴퓨터는 Data를 폐기함</p>
</li>
<li><p>물리 주소는 하나의 물리 네트워크 내에서 <strong>고유</strong>해야 한다</p>
</li>
<li><p>물리 주소는 <strong>48bit(6 byte)</strong>, 16진법 숫자 12개로 구성되어 사용
<img src="https://velog.velcdn.com/images/l-wanderer01/post/d3ceb712-1f20-49b3-acfe-22306a045dd9/image.png" alt=""></p>
<h2 id="42-논리-주소">4.2 논리 주소</h2>
<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/086ff045-2c62-4d85-9cb8-b4efd5a430ba/image.png" alt=""></p>
<blockquote>
<p>🚨 물리 주소는 hop-to-hop delivery를 하며 변하지만, 논리 주소는 <strong>변하지 않는다</strong>!</p>
</blockquote>
</li>
<li><p>각 장치(컴퓨터 or 라우터)는 각 물리적 연결에 대해 논리 주소와 물리 주소를 쌍으로 갖는다.</p>
</li>
<li><p>라우터는 3개의 네트워크를 연결하고 있으므로, 3쌍(논리 주소, 물리 주소)의 주소를 가진다.</p>
</li>
</ul>
<h2 id="43-포트-주소">4.3 포트 주소</h2>
<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/68178074-3841-4ab2-918c-3fd094c8b560/image.png" alt=""></p>
<blockquote>
<p><strong>포트 주소</strong>도 hop-to-hop delivery할 때, 변하지 않는다.</p>
</blockquote>
<ul>
<li>두 컴퓨터가 동일한 응용 프로그램(a, j)를 사용하고 있지만, FTP를 사용하는 응용 프로그램의 경우, 하나는 <strong>Client 프로그램</strong>, 다른 하나는 <strong>Server 프로그램</strong>이기에 <em>포트 주소가 다를 수 있다는 것에 유의 </em>해야 한다.</li>
<li>포트 주소는 하나의 10진수로 표기 (0 ~ 65535, $2^{16}$개)
<img src="https://velog.velcdn.com/images/l-wanderer01/post/0ded223f-c41f-4914-b6d0-3cadf924bc8c/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[CI/CD 구축 과정과 자동화 경험 + Test code의 필요성]]></title>
            <link>https://velog.io/@l-wanderer01/CICD-%EA%B5%AC%EC%B6%95-%EA%B3%BC%EC%A0%95%EA%B3%BC-%EC%9E%90%EB%8F%99%ED%99%94-%EA%B2%BD%ED%97%98-Test-code%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1</link>
            <guid>https://velog.io/@l-wanderer01/CICD-%EA%B5%AC%EC%B6%95-%EA%B3%BC%EC%A0%95%EA%B3%BC-%EC%9E%90%EB%8F%99%ED%99%94-%EA%B2%BD%ED%97%98-Test-code%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1</guid>
            <pubDate>Sat, 15 Feb 2025 09:38:11 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/l-wanderer01/post/8088092a-eda8-42a2-be91-2d3e491f5482/image.png" alt=""></p>
<h2 id="🔗-cicd-구축의-필요성">🔗 CI/CD 구축의 필요성</h2>
<p>3주간 팀원들과 함께한 프로젝트를 배포한 상태로 배포된 사이트에서 모든 기능들과 UI/UX가 정상적으로 반영되었는지 확인하고 테스트하는 과정에서 Git push, Git pull을 자주 반복하다보니 이를 자동화하는게 필요하다고 느꼈고, CI/CD를 구축 해야겠다고 생각했다.</p>
<blockquote>
<p>_나는 이번 프로젝트를 진행하며 CI/CD라는 것을 들어만 보았고, 이게 무엇을 의미하고 어떤 기능을 하는지 전혀 몰랐다...
CI/CD 파이프라인 구축을 담당한 나와 팀원 한분과 총 두 명이서 작업을 하였다.
<code>Github Actions는 함께 공부하고, 나는 배포 버전의 안정화를 위해 test 코드를 작성하였고, 팀원은 배포 자동화를 위해 Actions의 Settings 설정과 code를 작성하는 것으로 분업하였다.</code>
_</p>
</blockquote>
<h3 id="❓-cicd-파이프라인이란">❓ CI/CD 파이프라인이란</h3>
<p><strong>개념</strong>: CI/CD 파이프라인은 지속적 통합과 지속적 제공을 결합하여 코딩, 테스트, 배포 전반에 걸쳐 소프트웨어 제공을 자동화합니다.</p>
<blockquote>
<p>이 말을 처음 들었을 땐 와닿지 않았다. <em>&#39;자동화가 왜 필요하지?&#39;</em> 라는 생각을 했지만 프로젝트를 진행하고 배포를 완료하니 수정을 할 때마다 git의 main 브랜치에 push 혹은 pull request를 날려야하고, 서버 컴퓨터에 직접 접근을 해 main 브랜치의 내용을 pull 받아야하는 번거로움이 있었다. 이를 찾아보니 <code>CI/CD 파이프라인</code>을 구축한다는 이야기가 있었고, 따라서 CI/CD 파이프라인을 구축하였다.</p>
</blockquote>
<h3 id="☀️-cicontinuous-integration-지속적인-통합">☀️ CI(Continuous Integration): 지속적인 통합</h3>
<p><code>지속적인 통합</code>이란 수정한 코드를  배포하기 위해 git 브랜치에 push를 하는 행위로 코드를 병합하는 과정을 의미한다. 이에 따라 test를 진행하고 conflict를 발견하는 단계를 포함한다.</p>
<h3 id="🌕-cdcontinuous-deliverydeployment-지속적인-배포">🌕 CD(Continuous Delivery/Deployment): 지속적인 배포</h3>
<p><code>지속적인 배포</code>란 CI 단계에서 test를 통과한 후 실제로 서버 컴퓨터에 접속을 해 실제 서비스에 반영을 하는 과정을 의미한다.</p>
<h2 id="🔨-cicd-구축-툴">🔨 CI/CD 구축 툴</h2>
<p>찾아보니 CI/CD를 구축하기 위한 툴에 주로 많이 사용하는 것이 Github Actions, Jenkins를 사용하는 것 같다. 우리는 Github을 많이 사용해서 더욱 익숙한 <code>Github Actions</code>를 사용하기로 했다.
<img src="https://velog.velcdn.com/images/l-wanderer01/post/9c99252c-45ed-4f3a-8735-9a1cc248a883/image.png" alt="">
Github Actions를 사용하기 위해선 Actions에서 개발한 프로젝트에 적절한 템플릿을 고르고, Settings에서 적절한 설정을 해줘야 했다.</p>
<h3 id="1-settings-설정">1. Settings 설정</h3>
<p><code>Settings ➡️ Security ➡️ Secrets and variables ➡️ Actions</code></p>
<ul>
<li>New Repository Secret을 클릭하면 새로운 시크릿을 생성할 수 있다.
이때 생성하는 것들은 Git에 올라가지 않도록 했던 &quot;.env&quot; 파일 안의 값들이라고 생각하면 된다.
<code>예를 들면, 서버의 IP 값, DB 명, DB 비밀번호 등이 있다.</code><blockquote>
<p>Name: 시크릿 이름, Secret: 보안이 중요한 값</p>
</blockquote>
</li>
</ul>
<h3 id="2-actions">2. Actions</h3>
<p>Secret을 모두 설정해주었다면 Actions에 들어가 적절한 템플릿을 골라주면 된다.
우리가 개발한 프로젝트는 Django를 이용해 만들어서 Django 템플릿을 이용했다.</p>
<pre><code>name: Django CI

on:
  push:
    branches: [ &quot;main&quot; ]
  pull_request:
    branches: [ &quot;main&quot; ]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      max-parallel: 4
      matrix:
        python-version: [3.7, 3.8, 3.9]

    steps:
    - uses: actions/checkout@v4
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v3
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install Dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Run Tests
      run: |
        python manage.py test
</code></pre><ul>
<li>위의 내용이 Django 템플릿을 고르면 나오는 코드이다.
<del>~</del></li>
</ul>
<ol>
<li>name: Git Actions의 이름</li>
<li>on: git이 수행할 명령과 브랜치 지정</li>
<li>jobs: 실제로 자동화해 수행할 작업들</li>
<li>jobs/build/name: 실행될 job의 이름</li>
<li>jobs/build/run: 실행될 job의 동작</li>
</ol>
<ul>
<li>크게 내가 이해한 코드들이다. 다른 부분들은 다른 블로그의 내용들을 참고<pre><code></code></pre></li>
</ul>
<p>위의 Actions에 코드의 안정성 높이기 위해 test 코드가 필요하다는 것을 바로 느꼈다.</p>
<blockquote>
<p>test 코드를 넣지 않으면 잘못된 코드가 자동으로 배포 버전에 반영이 되어 에러를 일으킬 수 있다라고 직감적으로 느끼고, 급하게 test 코드를 작성했다...😵‍💫 다음부터는 기능을 개발하면 test 코드도 함께 개발해야겠다...😭</p>
</blockquote>
<p>Django에서는 test 코드를 서비스를 만들며 만든 각 App마다 tests.py가 생성되고 그 곳에 적절한 코드를 작성하면 된다!</p>
<ul>
<li><strong>test 코드를 작성하면 생기는 장점!!</strong> : 기능 개발 시에 발견하지 못했던 예외에 대한 부분들을 test에서 찾을 수 있다! ➡️ <code>이 점을 느끼고 다음부터 기능 개발할 때엔 test 코드도 함께 작성하려한다!</code></li>
</ul>
<p>test 코드를 작성하는데 애를 먹었지만 결국 모든 테스트를 통과시키는 것을 확인했고, Github Actions 코드도 적절한 명령들을 넣어 CI/CD를 완료하였다.</p>
<h2 id="✍️-마무리하며">✍️ 마무리하며</h2>
<blockquote>
<p>학교를 다니며 주변 사람들이 CI/CD를 해보는 경험이 실무에서 큰 도움이 된다는 말을 자주 들었는데, 이번 프로젝트를 하며 CI/CD라는 이름만 알고 있던 내가 CI/CD를 구축하며 한 단계 성장하는 좋은 경험을 하였다.🍀 다음에 CI/CD를 구축할 기회가 있다면 그때는 <code>Jenkins</code>를 사용해 봐야겠다!!
그리고 앞으로 기능 개발을 해나갈 때, test 코드도 함께 작성하며 예외처리 및 안전한 코드를 개발해나갈 것!😎</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>