<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>choish_dev.log</title>
        <link>https://velog.io/</link>
        <description>Full-Stack Developer</description>
        <lastBuildDate>Mon, 12 Jan 2026 00:17:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>choish_dev.log</title>
            <url>https://velog.velcdn.com/images/choish_dev/profile/99b545ea-1396-4835-b7d6-b69b21c5cf3e/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. choish_dev.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/choish_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[MyBatis란?]]></title>
            <link>https://velog.io/@choish_dev/MyBatis%EB%9E%80</link>
            <guid>https://velog.io/@choish_dev/MyBatis%EB%9E%80</guid>
            <pubDate>Mon, 12 Jan 2026 00:17:49 GMT</pubDate>
            <description><![CDATA[<p>MyBatis는 Java에서 SQL을 직접 작성하여 데이터베이스와 연동할 수 있게 해주는 SQL Mapper 프레임워크이다.
ORM(JPA)처럼 객체 중심이 아니라, SQL 중심 개발을 지향한다.</p>
<blockquote>
<p>👉 “SQL은 내가 짠다. 대신 Java 객체와의 매핑은 MyBatis가 해준다.”</p>
</blockquote>
<hr>
<h3 id="mybatis를-사용하는-이유">MyBatis를 사용하는 이유</h3>
<h4 id="✅-장점">✅ 장점</h4>
<ul>
<li><p>SQL을 직접 작성 → 쿼리 제어력 최상</p>
</li>
<li><p>기존 DB, 레거시 SQL과 궁합이 좋음</p>
</li>
<li><p>복잡한 JOIN / 서브쿼리 처리에 강함</p>
</li>
<li><p>성능 튜닝이 쉬움</p>
</li>
</ul>
<h4 id="❌-단점">❌ 단점</h4>
<ul>
<li><p>SQL을 직접 관리해야 함</p>
</li>
<li><p>코드량이 JPA보다 많음</p>
</li>
<li><p>DB 종속적인 SQL이 될 가능성 있음</p>
</li>
</ul>
<hr>
<h3 id="mybatis-vs-jpa-간단-비교">MyBatis vs JPA 간단 비교</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>MyBatis</th>
<th>JPA</th>
</tr>
</thead>
<tbody><tr>
<td>개발 방식</td>
<td>SQL 중심</td>
<td>객체(Entity) 중심</td>
</tr>
<tr>
<td>SQL 작성</td>
<td>직접 작성</td>
<td>자동 생성</td>
</tr>
<tr>
<td>복잡한 쿼리</td>
<td>매우 강함</td>
<td>상대적으로 불리</td>
</tr>
<tr>
<td>러닝 커브</td>
<td>낮음</td>
<td>높음</td>
</tr>
<tr>
<td>실무 사용</td>
<td>레거시/운영</td>
<td>신규 서비스</td>
</tr>
</tbody></table>
<hr>
<h3 id="mybatis-전체-구조">MyBatis 전체 구조</h3>
<pre><code class="language-scss">Controller
   ↓
Service
   ↓
Mapper Interface (DAO)
   ↓
Mapper XML (SQL)
   ↓
Database (MSSQL)
</code></pre>
<hr>
<h3 id="mybatis-핵심-구성-요소">MyBatis 핵심 구성 요소</h3>
<h4 id="1️⃣-mapper-interface-dao">1️⃣ Mapper Interface (DAO)</h4>
<pre><code class="language-java">@Mapper
public interface UserMapper {
    int insertUser(UserDTO user);
    UserDTO selectUserById(Long id);
}</code></pre>
<ul>
<li>SQL과 Java를 연결하는 인터페이스</li>
<li>실제 구현체는 MyBatis가 자동 생성</li>
</ul>
<hr>
<h4 id="2️⃣-mapper-xml-sql-작성-공간">2️⃣ Mapper XML (SQL 작성 공간)</h4>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&lt;!DOCTYPE mapper
  PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;
  &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;

&lt;mapper namespace=&quot;com.example.mapper.UserMapper&quot;&gt;

    &lt;insert id=&quot;insertUser&quot; parameterType=&quot;UserDTO&quot;&gt;
        INSERT INTO users (name, email)
        VALUES (#{name}, #{email})
    &lt;/insert&gt;

    &lt;select id=&quot;selectUserById&quot; parameterType=&quot;long&quot; resultType=&quot;UserDTO&quot;&gt;
        SELECT *
        FROM users
        WHERE id = #{id}
    &lt;/select&gt;

&lt;/mapper&gt;</code></pre>
<p>✔ SQL을 XML로 명확하게 분리
✔ #{} → 파라미터 바인딩 (SQL Injection 방지)</p>
<hr>
<h4 id="3️⃣-dto-data-transfer-object">3️⃣ DTO (Data Transfer Object)</h4>
<pre><code class="language-java">@Getter
@Setter
public class UserDTO {
    private Long id;
    private String name;
    private String email;
}</code></pre>
<ul>
<li>MyBatis는 Entity 개념이 없음</li>
<li>대신 DTO/VO로 데이터 전달</li>
</ul>
<hr>
<h4 id="spring-boot--mybatis-설정">Spring Boot + MyBatis 설정</h4>
<p><strong>application.yml</strong></p>
<pre><code class="language-yaml">mybatis:
  mapper-locations: classpath:/mapper/**/*.xml
  type-aliases-package: com.example.domain</code></pre>
<hr>
<h3 id="mybatis-동작-흐름-중요-⭐">MyBatis 동작 흐름 (중요 ⭐)</h3>
<p>1️⃣ Controller에서 요청
2️⃣ Service에서 비즈니스 로직 처리
3️⃣ Mapper Interface 호출
4️⃣ Mapper XML의 SQL 실행
5️⃣ 결과를 DTO로 매핑
6️⃣ Service → Controller 반환</p>
<hr>
<h3 id="mybatis-파라미터-처리">MyBatis 파라미터 처리</h3>
<h4 id="단일-파라미터">단일 파라미터</h4>
<pre><code class="language-sql">WHERE id = #{id}</code></pre>
<h4 id="객체-파라미터">객체 파라미터</h4>
<pre><code class="language-sql">WHERE name = #{name}
AND email = #{email}</code></pre>
<h4 id="여러-파라미터-param">여러 파라미터 (@Param)</h4>
<pre><code class="language-java">UserDTO find(@Param(&quot;name&quot;) String name,
             @Param(&quot;email&quot;) String email);</code></pre>
<pre><code class="language-sql">WHERE name = #{name}
AND email = #{email}</code></pre>
<hr>
<h3 id="동적-sql-mybatis의-강력한-기능">동적 SQL (MyBatis의 강력한 기능)</h3>
<h4 id="조건에-따라-sql-변경">조건에 따라 SQL 변경</h4>
<pre><code class="language-xml">&lt;select id=&quot;findUser&quot; resultType=&quot;UserDTO&quot;&gt;
    SELECT * FROM users
    &lt;where&gt;
        &lt;if test=&quot;name != null&quot;&gt;
            AND name = #{name}
        &lt;/if&gt;
        &lt;if test=&quot;email != null&quot;&gt;
            AND email = #{email}
        &lt;/if&gt;
    &lt;/where&gt;
&lt;/select&gt;</code></pre>
<p>✔ 필요할 때만 조건 추가
✔ 복잡한 검색 조건에 매우 유리</p>
<hr>
<h3 id="mybatis에서-insert-결과-확인">MyBatis에서 insert 결과 확인</h3>
<pre><code class="language-java">int count = userMapper.insertUser(user);

if (count &gt; 0) {
    log.info(&quot;저장 성공&quot;);
}</code></pre>
<ul>
<li>반환값 = 영향받은 row 수</li>
<li>저장 성공 여부 판단에 자주 사용</li>
</ul>
<hr>
<h3 id="mybatis는-repository가-없다">MyBatis는 Repository가 없다?</h3>
<p>✔ 맞다.
MyBatis에서는 보통</p>
<pre><code class="language-ini">Mapper = DAO = Repository 역할</code></pre>
<p>즉,
@Repository 대신 @Mapper 를 사용한다.</p>
<hr>
<h3 id="정리">정리</h3>
<ul>
<li><p>MyBatis는 SQL 중심 데이터 접근 프레임워크</p>
</li>
<li><p>JPA보다 자유도와 성능 제어력이 뛰어남</p>
</li>
<li><p>실무, 운영 시스템에서 매우 많이 사용</p>
</li>
<li><p>Spring Boot + MSSQL 조합에서 특히 강력</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] MSSQL이란? – Microsoft SQL Server 개념 정리]]></title>
            <link>https://velog.io/@choish_dev/DB-MSSQL%EC%9D%B4%EB%9E%80-Microsoft-SQL-Server-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@choish_dev/DB-MSSQL%EC%9D%B4%EB%9E%80-Microsoft-SQL-Server-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Fri, 09 Jan 2026 06:47:08 GMT</pubDate>
            <description><![CDATA[<h2 id="1️⃣-mssql이란">1️⃣ MSSQL이란?</h2>
<p><strong>MSSQL(Microsoft SQL Server)</strong>은
마이크로소프트(Microsoft)에서 개발한 <strong>관계형 데이터베이스 관리 시스템(RDBMS)</strong>이다.</p>
<p>주로 기업용 시스템, 사내 인트라넷, Windows 서버 환경에서 많이 사용되며
대규모 트랜잭션 처리와 안정성을 강점으로 가진 DB이다.</p>
<blockquote>
<p>✔ 줄여서 MS SQL, SQL Server라고도 부른다.</p>
</blockquote>
<hr>
<h2 id="2️⃣-mssql의-기본-구성">2️⃣ MSSQL의 기본 구성</h2>
<p>MSSQL을 사용하면 보통 아래 구성으로 개발하게 된다.</p>
<h4 id="🔹-sql-server-db-엔진">🔹 SQL Server (DB 엔진)</h4>
<ul>
<li><p>실제 데이터를 저장하고 처리하는 핵심 서버</p>
</li>
<li><p>백엔드(Spring Boot 등)와 직접 연결되는 대상</p>
</li>
</ul>
<h4 id="🔹-ssms-sql-server-management-studio">🔹 SSMS (SQL Server Management Studio)</h4>
<ul>
<li><p>MSSQL 전용 관리 도구</p>
</li>
<li><p>테이블 생성, 쿼리 실행, 데이터 확인, 인덱스 관리 등을 수행</p>
<pre><code class="language-text">Spring Boot ── JDBC/JPA ── SQL Server ── 실제 데이터
                  ↑
                SSMS
</code></pre>
</li>
</ul>
<pre><code>&gt; 👉 실무에서는
- 개발자는 SSMS로 테이블 구조 확인
- 애플리케이션에서는 Spring Boot에서 쿼리 실행

---

## 3️⃣ MSSQL의 주요 특징
#### ✅ 1. 기업용 DB에 특화

- 금융, 공공기관, 사내 시스템에서 많이 사용

- 대용량 데이터 처리와 안정성이 강점

#### ✅ 2. Transact-SQL (T-SQL) 사용

- MSSQL 전용 SQL 문법 제공

- 변수 선언, 조건문, 반복문 가능
```sql
DECLARE @count INT;
SET @count = 10;

SELECT @count;
</code></pre><blockquote>
<p>👉 MySQL에서는 거의 쓰지 않는 문법</p>
</blockquote>
<h4 id="✅-3-windows-친화적인-환경">✅ 3. Windows 친화적인 환경</h4>
<ul>
<li><p>Windows Server + Active Directory와 궁합이 좋음</p>
</li>
<li><p>사내 계정 기반 보안 관리가 쉬움</p>
</li>
</ul>
<h4 id="✅-4-강력한-트랜잭션-관리">✅ 4. 강력한 트랜잭션 관리</h4>
<ul>
<li><p>BEGIN TRANSACTION</p>
</li>
<li><p>COMMIT</p>
</li>
<li><p>ROLLBACK</p>
<pre><code class="language-sql">BEGIN TRANSACTION;
</code></pre>
</li>
</ul>
<p>INSERT INTO TB_USER VALUES (&#39;test&#39;, &#39;1234&#39;);</p>
<p>ROLLBACK;</p>
<pre><code>
---

## 8️⃣ MSSQL 기본 문법 (T-SQL 핵심 정리)

MSSQL은 표준 SQL + T-SQL(Transact-SQL) 문법을 사용한다.
MySQL과 비슷하지만 변수, 조건문, 트랜잭션, TOP, IDENTITY 같은 MSSQL 전용 문법이 많다.

#### 🔹 1. 데이터 조회 (SELECT)
```sql
SELECT * FROM TB_USER;
</code></pre><ul>
<li><strong>특정 컬럼 조회</strong><pre><code class="language-sql">SELECT user_id, user_name
FROM TB_USER;</code></pre>
</li>
<li><strong>조건 조회</strong></li>
</ul>
<pre><code class="language-sql">SELECT *
FROM TB_USER
WHERE user_id = &#39;admin&#39;;</code></pre>
<h4 id="🔹-2-top-mssql-전용">🔹 2. TOP (MSSQL 전용)</h4>
<blockquote>
<p>👉 LIMIT 대신 TOP 사용</p>
</blockquote>
<pre><code class="language-sql">SELECT TOP 10 *
FROM TB_BOARD
ORDER BY created_at DESC;
</code></pre>
<ul>
<li><strong>MySQL 비교</strong></li>
</ul>
<pre><code class="language-sql">-- MySQL
SELECT * FROM TB_BOARD LIMIT 10;</code></pre>
<h4 id="🔹-3-insert--update--delete">🔹 3. INSERT / UPDATE / DELETE</h4>
<ul>
<li><strong>INSERT</strong><pre><code class="language-sql">INSERT INTO TB_USER (user_id, password, name)
VALUES (&#39;test&#39;, &#39;1234&#39;, &#39;홍길동&#39;);
</code></pre>
</li>
</ul>
<pre><code>- **UPDATE**
```sql
UPDATE TB_USER
SET name = &#39;김철수&#39;
WHERE user_id = &#39;test&#39;;</code></pre><ul>
<li><strong>DELETE</strong><pre><code class="language-sql">DELETE FROM TB_USER
WHERE user_id = &#39;test&#39;;</code></pre>
</li>
</ul>
<h4 id="🔹-4-변수-선언-t-sql-핵심">🔹 4. 변수 선언 (T-SQL 핵심)</h4>
<blockquote>
<p>👉 MSSQL에서 매우 자주 사용</p>
</blockquote>
<pre><code class="language-sql">DECLARE @userCount INT;

SET @userCount = (
    SELECT COUNT(*)
    FROM TB_USER
);

SELECT @userCount AS userCount;
</code></pre>
<p>✔ MySQL에는 거의 없는 문법
✔ 프로시저, 동적 SQL, 배치 작업에서 필수</p>
<h4 id="🔹-5-if--else-조건문">🔹 5. IF / ELSE 조건문</h4>
<pre><code class="language-sql">DECLARE @count INT;
SET @count = 5;

IF @count &gt; 0
    PRINT &#39;데이터 있음&#39;;
ELSE
    PRINT &#39;데이터 없음&#39;;
</code></pre>
<h4 id="🔹-6-트랜잭션-실무-핵심-⭐">🔹 6. 트랜잭션 (실무 핵심 ⭐)</h4>
<pre><code class="language-sql">BEGIN TRANSACTION;

INSERT INTO TB_ORDER VALUES (&#39;order1&#39;, 1000);
INSERT INTO TB_PAYMENT VALUES (&#39;pay1&#39;, 1000);

-- 문제가 없으면
COMMIT;

-- 오류 발생 시
ROLLBACK;
</code></pre>
<blockquote>
<p>👉 Spring Boot의 @Transactional과 개념 동일</p>
</blockquote>
<h4 id="🔹-7-자동-증가-컬럼-identity">🔹 7. 자동 증가 컬럼 (IDENTITY)</h4>
<pre><code class="language-sql">CREATE TABLE TB_BOARD (
    board_id INT IDENTITY(1,1) PRIMARY KEY,
    title NVARCHAR(100),
    content NVARCHAR(MAX)
);
</code></pre>
<table>
<thead>
<tr>
<th>구분</th>
<th>MSSQL</th>
<th>MySQL</th>
</tr>
</thead>
<tbody><tr>
<td>자동 증가</td>
<td>IDENTITY</td>
<td>AUTO_INCREMENT</td>
</tr>
</tbody></table>
<h4 id="🔹-8-날짜-함수">🔹 8. 날짜 함수</h4>
<pre><code class="language-sql">SELECT GETDATE();        -- 현재 날짜 + 시간
SELECT CONVERT(DATE, GETDATE()); -- 날짜만
</code></pre>
<h4 id="🔹-9-null-처리-is-null">🔹 9. NULL 처리 (IS NULL)</h4>
<pre><code class="language-sql">SELECT *
FROM TB_USER
WHERE phone IS NULL;
</code></pre>
<h4 id="🔹-10-like-검색">🔹 10. LIKE 검색</h4>
<pre><code class="language-sql">SELECT *
FROM TB_USER
WHERE name LIKE &#39;%홍%&#39;;
</code></pre>
<h4 id="🔹-11-join">🔹 11. JOIN</h4>
<pre><code class="language-sql">SELECT u.user_id, o.order_id
FROM TB_USER u
INNER JOIN TB_ORDER o
ON u.user_id = o.user_id;
</code></pre>
<h4 id="🔹-12-스키마dbo란">🔹 12. 스키마(dbo)란?</h4>
<pre><code class="language-sql">SELECT *
FROM dbo.TB_USER;
</code></pre>
<ul>
<li>dbo = 기본 스키마</li>
<li>테이블 소유자 개념</li>
<li>명시하지 않으면 자동으로 dbo 사용됨</li>
</ul>
<h4 id="🔹-13-대괄호--문법">🔹 13. 대괄호 [] 문법</h4>
<pre><code class="language-sql">SELECT *
FROM [User];
</code></pre>
<p>✔ 예약어 사용
✔ 공백 포함 컬럼명
✔ 기존 테이블 스크립트에 자주 등장</p>
<hr>
<h2 id="4️⃣-mssql-vs-mysql-차이">4️⃣ MSSQL vs MySQL 차이</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>MSSQL</th>
<th>MySQL</th>
</tr>
</thead>
<tbody><tr>
<td>개발사</td>
<td>Microsoft</td>
<td>Oracle</td>
</tr>
<tr>
<td>주 사용처</td>
<td>기업/사내 시스템</td>
<td>웹 서비스</td>
</tr>
<tr>
<td>OS 친화성</td>
<td>Windows 중심</td>
<td>Linux 중심</td>
</tr>
<tr>
<td>SQL 문법</td>
<td>T-SQL</td>
<td>표준 SQL</td>
</tr>
<tr>
<td>관리 도구</td>
<td>SSMS</td>
<td>Workbench</td>
</tr>
<tr>
<td>라이선스</td>
<td>유료 중심</td>
<td>무료 중심</td>
</tr>
</tbody></table>
<hr>
<h2 id="6️⃣-spring-boot에서-mssql-사용-흐름">6️⃣ Spring Boot에서 MSSQL 사용 흐름</h2>
<ol>
<li>MSSQL + SSMS 설치</li>
<li>데이터베이스 &amp; 테이블 생성</li>
<li>Spring Boot에 JDBC 드라이버 추가</li>
<li>application.yml 또는 properties 설정</li>
<li>JPA / MyBatis / JDBC로 연동<pre><code class="language-yaml">spring:
datasource:
 url: jdbc:sqlserver://localhost:1433;databaseName=testdb
 username: sa
 password: ****
</code></pre>
</li>
</ol>
<p>```</p>
<hr>
<h2 id="7️⃣-정리">7️⃣ 정리</h2>
<p>✔ MSSQL은 기업 환경에서 매우 많이 사용되는 DB
✔ T-SQL이라는 강력한 전용 문법 제공
✔ Spring Boot와도 문제없이 연동 가능
✔ 실무 백엔드 개발자라면 알아두면 반드시 도움 되는 DB</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Spring Boot란?]]></title>
            <link>https://velog.io/@choish_dev/Java-Spring-Boot%EB%9E%80</link>
            <guid>https://velog.io/@choish_dev/Java-Spring-Boot%EB%9E%80</guid>
            <pubDate>Thu, 08 Jan 2026 07:43:35 GMT</pubDate>
            <description><![CDATA[<h2 id="spring-boot란">Spring Boot란?</h2>
<p>백엔드 개발을 공부하다 보면 가장 많이 듣게 되는 단어 중 하나가 바로 Spring Boot다.
그렇다면 Spring Boot는 정확히 무엇이고, 왜 이렇게 많이 사용될까?</p>
<p>이번 글에서는 Spring Boot의 개념, 등장 배경, 특징, 그리고 왜 사용하는지를 정리해본다.</p>
<hr>
<h3 id="spring-boot란-1">Spring Boot란?</h3>
<p>👉 Spring Framework를 더 쉽고 빠르게 사용하기 위해 만들어진 프레임워크이다.</p>
<p>기존 Spring은 강력하지만,</p>
<ul>
<li><p>설정 파일이 많고</p>
</li>
<li><p>초기 환경 설정이 복잡하며</p>
</li>
<li><p>서버 설정까지 직접 해야 하는 경우가 많았다</p>
</li>
</ul>
<p>이런 불편함을 해결하기 위해 등장한 것이 Spring Boot다.</p>
<hr>
<h3 id="spring-boot가-등장한-이유">Spring Boot가 등장한 이유</h3>
<p>기존 Spring Framework의 문제점은 다음과 같았다.</p>
<ul>
<li><p>XML 설정 파일이 너무 많음</p>
</li>
<li><p>프로젝트 시작 전에 해야 할 설정이 많음</p>
</li>
<li><p>톰캣 같은 WAS를 따로 설치해야 함</p>
</li>
<li><p>작은 기능 하나 만드는데도 준비 과정이 김</p>
</li>
</ul>
<p>👉 Spring Boot는 이 모든 과정을 자동화했다.</p>
<hr>
<h3 id="spring-boot의-핵심-특징">Spring Boot의 핵심 특징</h3>
<p><strong>1️⃣ 자동 설정 (Auto Configuration)</strong></p>
<p>Spring Boot는
의존성만 추가하면 필요한 설정을 자동으로 처리해준다.</p>
<pre><code class="language-text">spring-boot-starter-web 추가
→ Tomcat 자동 설정
→ DispatcherServlet 자동 등록
→ MVC 설정 자동 완료</code></pre>
<p>✔️ 개발자는 설정이 아니라 비즈니스 로직에만 집중하면 된다.</p>
<hr>
<p><strong>2️⃣ 내장 서버 (Embedded Server)</strong></p>
<p>Spring Boot는 내장 Tomcat을 포함하고 있다.</p>
<pre><code class="language-bash">java -jar app.jar</code></pre>
<p>이 명령어 하나로 서버 실행 가능 ❗</p>
<p>✔️ 별도의 WAS 설치 불필요
✔️ 배포가 매우 간단</p>
<hr>
<p><strong>3️⃣ Starter Dependency</strong></p>
<p>Spring Boot는 자주 쓰는 라이브러리를 묶어서 제공한다.</p>
<table>
<thead>
<tr>
<th>Starter</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>spring-boot-starter-web</td>
<td>웹 개발 (MVC, Tomcat 포함)</td>
</tr>
<tr>
<td>spring-boot-starter-data-jpa</td>
<td>JPA + Hibernate</td>
</tr>
<tr>
<td>spring-boot-starter-security</td>
<td>Spring Security</td>
</tr>
<tr>
<td>spring-boot-starter-test</td>
<td>테스트 관련 라이브러리</td>
</tr>
</tbody></table>
<p>✔️ 의존성 충돌 걱정 ↓
✔️ 설정 시간 ↓</p>
<hr>
<p><strong>4️⃣ 설정의 단순화 (application.yml / properties)</strong></p>
<p>과거 XML 설정 대신
application.yml 또는 application.properties 파일 하나로 설정 관리</p>
<pre><code class="language-yml">server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test</code></pre>
<p>✔️ 가독성 좋음
✔️ 환경별 설정 분리 가능 (dev, prod)</p>
<hr>
<h3 id="spring-vs-spring-boot-차이">Spring vs Spring Boot 차이</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>Spring</th>
<th>Spring Boot</th>
</tr>
</thead>
<tbody><tr>
<td>설정</td>
<td>수동 설정 많음</td>
<td>자동 설정</td>
</tr>
<tr>
<td>서버</td>
<td>외부 WAS 필요</td>
<td>내장 서버</td>
</tr>
<tr>
<td>시작 난이도</td>
<td>높음</td>
<td>낮음</td>
</tr>
<tr>
<td>생산성</td>
<td>보통</td>
<td>매우 높음</td>
</tr>
</tbody></table>
<blockquote>
<p>👉 요즘 Spring 개발 = 거의 Spring Boot</p>
</blockquote>
<hr>
<h3 id="spring-boot는-언제-사용될까">Spring Boot는 언제 사용될까?</h3>
<ul>
<li><p>REST API 서버</p>
</li>
<li><p>React / Vue와 연동되는 백엔드</p>
</li>
<li><p>사내 시스템 (인트라넷)</p>
</li>
<li><p>스타트업 MVP 개발</p>
</li>
<li><p>마이크로서비스 아키텍처</p>
</li>
</ul>
<p>✔️ 실무 백엔드 개발의 표준이라고 봐도 무방하다.</p>
<hr>
<h3 id="간단한-spring-boot-실행-구조">간단한 Spring Boot 실행 구조</h3>
<pre><code class="language-text">요청
 ↓
Controller
 ↓
Service
 ↓
Repository
 ↓
Database</code></pre>
<p>Spring Boot는 이 구조를 가장 빠르게 만들 수 있도록 도와주는 도구다.</p>
<hr>
<h2 id="마무리">마무리</h2>
<p>Spring Boot는</p>
<ul>
<li><p>Spring의 장점은 그대로 유지하면서</p>
</li>
<li><p>복잡한 설정을 제거하고</p>
</li>
<li><p>빠른 개발과 쉬운 배포를 가능하게 만든 프레임워크다.</p>
</li>
</ul>
<p>👉 백엔드 개발을 시작한다면, Spring Boot는 거의 필수 선택지라고 할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 스택(Stack) 이란?]]></title>
            <link>https://velog.io/@choish_dev/JAVA-%EC%8A%A4%ED%83%9DStack-%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@choish_dev/JAVA-%EC%8A%A4%ED%83%9DStack-%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Sat, 27 Dec 2025 08:06:27 GMT</pubDate>
            <description><![CDATA[<h2 id="💡스택이란">💡스택이란?</h2>
<p>스택(Stack)은 한쪽(top)에서만 원소의 삽입과 삭제가 이루어지는 자료구조입니다.
특징적으로 LIFO(Last-In-First-Out), 즉 후입선출 구조를 가집니다.</p>
<p>예시:</p>
<pre><code>push A
push B
push C
pop → C</code></pre><blockquote>
<p>💡 가장 먼저 들어간 A는 가장 마지막에 꺼내질 수 있다.</p>
</blockquote>
<hr>
<h3 id="-스택의-주요-연산">※ 스택의 주요 연산</h3>
<table>
<thead>
<tr>
<th>연산</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>push</td>
<td>스택 top에 데이터를 삽입</td>
</tr>
<tr>
<td>pop</td>
<td>top 데이터를 꺼내고 삭제</td>
</tr>
<tr>
<td>peek</td>
<td>top 데이터를 확인만</td>
</tr>
<tr>
<td>isEmpty</td>
<td>스택이 비었는지 확인</td>
</tr>
<tr>
<td>remove</td>
<td>삭제만 수행(pop과 다르게 값 반환 x)</td>
</tr>
</tbody></table>
<h4 id="-adt-형식-예시">※ ADT 형식 예시</h4>
<pre><code class="language-scss">createStack()
push(item)
pop()
peek()
isEmpty()
remove()
</code></pre>
<hr>
<h3 id="-스택의-구현-방식">※ 스택의 구현 방식</h3>
<p>① 배열(Array)을 이용한 순차적 구현</p>
<ul>
<li><p>top 값으로 가장 위를 가리킨다.</p>
</li>
<li><p>초기 top = -1</p>
</li>
<li><p>push 시 top++</p>
</li>
<li><p>pop 시 top--</p>
</li>
</ul>
<blockquote>
<p>구조:</p>
</blockquote>
<pre><code>Stack[0] [1] [2] ... [n]
   ↑
  top</code></pre><p><strong>- 장점: 간단함</strong>
<strong>- 단점: 크기 제한 존재</strong></p>
<p>② 연결리스트(Linked List)를 이용한 구현</p>
<ul>
<li><p>메모리 동적 할당 가능</p>
</li>
<li><p>top이 연결 리스트의 head 역할</p>
</li>
</ul>
<blockquote>
<p>구조:</p>
</blockquote>
<pre><code class="language-scss">top → Node(data, link) → Node → ...</code></pre>
<ul>
<li>push: 새 노드를 top 앞에 삽입</li>
<li>pop: top을 삭제 후 link를 top으로 변경
<br><strong>- 장점: 크기 제한 없음</strong></li>
<li><em>- 단점: 구현 약간 복잡*</em></li>
</ul>
<hr>
<h3 id="-스택의-대표적-활용">※ 스택의 대표적 활용</h3>
<h4 id="✔-수식의-괄호-검사">✔ 수식의 괄호 검사</h4>
<pre><code class="language-css">{ [ ( ) ] }</code></pre>
<ul>
<li><p>여는 괄호 push</p>
</li>
<li><p>닫는 괄호 pop으로 비교</p>
</li>
<li><p>empty &amp; mismatch 판별</p>
</li>
</ul>
<hr>
<h4 id="✔-후위-표기법postfix-계산">✔ 후위 표기법(Postfix) 계산</h4>
<blockquote>
<p>중위: A + B * C
후위: A B C * +</p>
</blockquote>
<p>스택 단계:</p>
<ol>
<li><p>피연산자 push</p>
</li>
<li><p>연산자 만나면 pop 두 번 → 연산 → push</p>
</li>
</ol>
<hr>
<h4 id="✔-실행-시간-스택-추가-응용">✔ 실행 시간 스택 (추가 응용)</h4>
<p>함수 호출 → 스택 push
함수 종료 → pop</p>
<ul>
<li><p>활성화 레코드 관리</p>
</li>
<li><p>재귀 호출 시 스택 증가</p>
</li>
</ul>
<hr>
<h4 id="✔-미로-탐색-dfs-방식">✔ 미로 탐색 (DFS 방식)</h4>
<p>좌표, 방향을 스택에 저장하며 백트래킹</p>
<hr>
<h3 id="📗-스택의-특징-정리">📗 스택의 특징 정리</h3>
<table>
<thead>
<tr>
<th>특징</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>LIFO</td>
<td>후입선출</td>
</tr>
<tr>
<td>top</td>
<td>한쪽 끝에서 삽입/삭제</td>
</tr>
<tr>
<td>push/pop</td>
<td>주 연산</td>
</tr>
<tr>
<td>구현</td>
<td>배열 / 연결리스트</td>
</tr>
<tr>
<td>활용</td>
<td>수식, 괄호, 후위 계산, 재귀, 미로</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] 단순연결 리스트]]></title>
            <link>https://velog.io/@choish_dev/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EB%8B%A8%EC%88%9C%EC%97%B0%EA%B2%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@choish_dev/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EB%8B%A8%EC%88%9C%EC%97%B0%EA%B2%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Thu, 20 Nov 2025 04:51:14 GMT</pubDate>
            <description><![CDATA[<h2 id="-단순연결-리스트란">※ 단순연결 리스트란?</h2>
<ul>
<li>하나의 링크 필드를 가진 노드들이 모두 자기 후속노드와 연결되어 있는 노드 열</li>
<li>마지막 노드의 링크 필드는 리스트의 끝을 표시하는 null값을 가짐</li>
<li>별칭 : 선형 연결 리스트(linear linked list), 단순 연결 선형 리스트(singly linked linear list), 연결 리스트(linked list), 체인(chain)</li>
<li>단순 연결 리스트의 예
<img src="https://velog.velcdn.com/images/choish_dev/post/03f85dbf-16f0-46ea-ae88-36ab077728ff/image.png" alt=""></li>
</ul>
<hr>
<br>

<h3 id="노드-구조-정의">노드 구조 정의</h3>
<h4 id="●-원소-삽입-알고리즘">● 원소 삽입 알고리즘</h4>
<ul>
<li>예) 리스트 L에 원소 “Han”을 “Cho”와 “Kim” 사이에 삽입<ol>
<li>공백노드를 획득함. newNode라는 변수로 가리키게 함</li>
<li>newNode의 data 필드에 “Han”을 저장</li>
<li>“Cho”를 저장하고 있는 노드의 link 값을 newNode의             link 필드에 저장 
(즉 “Kim”을 저장하고 있는 노드의 주소를 newNode의         link에 저장)</li>
<li>“Cho”를 저장한 노드의 link에 newNode의 포인터 값        을 저장</li>
</ol>
</li>
<li>리스트의 기존 원소들을 이동시킬 필요 없음</li>
<li>부수적인 link 필드를 위해 저장 공간을 추가로 사용
<img src="https://velog.velcdn.com/images/choish_dev/post/a0c72d65-918e-4e55-80a3-1bceff1505d8/image.png" alt=""></li>
</ul>
<br>

<h4 id="●-원소-삭제-알고리즘">● 원소 삭제 알고리즘</h4>
<ul>
<li>예) 리스트 L에서 원소 “Han”을 삭제<ol>
<li>원소 “Han”이 들어 있는 노드의 선행자 찾음 (“Cho”가         들어있는 노드)</li>
<li>이 선행자의 link에 “Han”이 들어있는 노드의 link 값을         저장
<img src="https://velog.velcdn.com/images/choish_dev/post/ae67b2c6-dbdd-40b4-9f05-393a2d02ba24/image.png" alt=""></li>
</ol>
</li>
</ul>
<br>

<h4 id="●-메모리의-획득과-반납">● 메모리의 획득과 반납</h4>
<ul>
<li><p>연결 리스트가 필요로 하는 두 가지 연산</p>
<blockquote>
<ul>
<li>데이타 필드와 하나의 링크 필드를 가진 하나의 공백 노드    를         획득하는 방법</li>
<li>사용하지 않는 노드는 다시 반납하여 재사용하는 방법</li>
</ul>
</blockquote>
</li>
<li><p>자유 공간 리스트 (free space list) 가 있는 경우</p>
<blockquote>
<ul>
<li>getNode()  </li>
</ul>
</blockquote>
<ul>
<li>데이타와 링크 필드로 되어 있는 새로운 공백 노드를             가용 공간 리스트로부터 할당받아 그 주소를 반환하는             함수<blockquote>
<ul>
<li>returnNode()</li>
</ul>
</blockquote>
</li>
<li>포인터 변수 P가 지시하는 노드를 가용 공간 리스트에 반    환하는 함수</li>
<li>프로그래밍 언어에 따라 필요한 경우도 있고 필요하지 않은     경우도 있음</li>
<li>Java와 같이 사용하지 못하는 노드를 자동으로 관리해 주    는 언어에서도 사용자가 직접 관리할 필요가 있는 경우도 있음</li>
</ul>
</li>
</ul>
<hr>
<h3 id="기본-연산">기본 연산</h3>
<h4 id="●-리스트-생성-알고리즘">● 리스트 생성 알고리즘</h4>
<ul>
<li><p>앞의 두 함수를 사용한 리스트 생성 알고리즘
<img src="https://velog.velcdn.com/images/choish_dev/post/fe61b8d9-bb0a-4599-a6f0-45956afdc07f/image.png" alt=""></p>
</li>
<li><p>리스트 생성 알고리즘을 점 표기식으로 작성한 경우
<img src="https://velog.velcdn.com/images/choish_dev/post/4a5de008-f1ea-4578-b67d-b988dcb05706/image.png" alt="">
<img src="https://velog.velcdn.com/images/choish_dev/post/512f18bf-146e-4b7c-8b5b-e9e6926efcad/image.png" alt=""></p>
</li>
</ul>
<h4 id="●-노드-삽입-알고리즘">● 노드 삽입 알고리즘</h4>
<ul>
<li><p>리스트 L의 첫번째 노드로 data 값이 x인 노드를 삽입
<img src="https://velog.velcdn.com/images/choish_dev/post/3ad5292e-7392-4c67-b14f-2c593d93c5f3/image.png" alt=""></p>
</li>
<li><p>원소값이 x인 노드를 p가 가리키는 노드 다음에 삽입
<img src="https://velog.velcdn.com/images/choish_dev/post/65287591-3712-4790-a5d3-618340523947/image.png" alt=""></p>
<blockquote>
<ul>
<li>(a) L이 공백 리스트인 경우
<img src="https://velog.velcdn.com/images/choish_dev/post/81346116-cae0-4426-81ea-0f147c069cb3/image.png" alt=""></li>
<li>(b) P가 null인 경우
<img src="https://velog.velcdn.com/images/choish_dev/post/46e880dc-3434-4799-98cd-613145953efe/image.png" alt=""></li>
<li>(c) L과 p가 null이 아닌 경우
<img src="https://velog.velcdn.com/images/choish_dev/post/1f6f7c5c-5013-4f7f-af12-1c285bd05f78/image.png" alt=""></li>
</ul>
</blockquote>
</li>
</ul>
<ul>
<li>리스트 L의 마지막 노드로 원소값 x를 첨가
<img src="https://velog.velcdn.com/images/choish_dev/post/fa58e235-3a71-4c10-a22e-49db940c59b7/image.png" alt=""></li>
</ul>
<h4 id="●-노드-삭제-알고리즘">● 노드 삭제 알고리즘</h4>
<ul>
<li><p>리스트 L에서 p가 가리키는 노드의 다음 노드를 삭제
<img src="https://velog.velcdn.com/images/choish_dev/post/c36ce711-edd9-4fec-b3fc-d546ced34872/image.png" alt=""></p>
</li>
<li><p>리스트에서 마지막 노드를 삭제하는 프로그램
<img src="https://velog.velcdn.com/images/choish_dev/post/b73567aa-828a-4ca5-b918-8e9a60068dc3/image.png" alt="">
<img src="https://velog.velcdn.com/images/choish_dev/post/23999960-590f-47e2-8129-01afbe86394f/image.png" alt=""></p>
<blockquote>
<ul>
<li>currentNode와 previousNode의 동작 과정
－ currentNode 포인터가 어떤 노드를 가리키면 previousNode 포인    터는 currentNode가 가리키는 노드의 바로 직전 노드를 가리키도록 함
－ currentNode가 리스트의 마지막 노드를 가리키게 될 때 previousNode는 마지막 두 번째 노드를 가리키게 됨
<img src="https://velog.velcdn.com/images/choish_dev/post/13e7c9fe-3912-4c54-b748-94dbccdbc53e/image.png" alt=""></li>
</ul>
</blockquote>
</li>
</ul>
<h4 id="●-리스트-역순-알고리즘">● 리스트 역순 알고리즘</h4>
<ul>
<li>리스트를 역순으로 만드는 알고리즘
<img src="https://velog.velcdn.com/images/choish_dev/post/b1e57bdf-68a3-4cf5-9fdf-9a1feac06f26/image.png" alt=""></li>
</ul>
<h4 id="●-리스트-연결-알고리즘">● 리스트 연결 알고리즘</h4>
<ul>
<li>두개의 리스트 L1과 L2를 하나로 만드는 알고리즘
<img src="https://velog.velcdn.com/images/choish_dev/post/34852a43-3ce6-4a8a-97d3-8dcb8988b69f/image.png" alt=""></li>
</ul>
<h4 id="●-원소-탐색-알고리즘">● 원소 탐색 알고리즘</h4>
<ul>
<li><p>데이타 값이 x인 노드를 찾는 알고리즘
<img src="https://velog.velcdn.com/images/choish_dev/post/7c5bcf16-fe6e-46a9-b198-22943b20f9a7/image.png" alt=""></p>
</li>
<li><p>데이타 값이 x인 노드를 찾는 알고리즘의 Java 구현
<img src="https://velog.velcdn.com/images/choish_dev/post/700299be-81bf-4cf8-a407-006cef04c584/image.png" alt=""></p>
</li>
</ul>
<h4 id="●-리스트-출력-알고리즘">● 리스트 출력 알고리즘</h4>
<ul>
<li>연결 리스트의 프린트
<img src="https://velog.velcdn.com/images/choish_dev/post/40fe75c5-58d9-4f4d-aa00-5dec26f50d35/image.png" alt=""></li>
</ul>
<hr>
<h3 id="💻-예제-프로그램">💻 예제 프로그램</h3>
<h4 id="●-listnode-클래스">● ListNode 클래스</h4>
<ul>
<li>data 필드와 link 필드를 가진 객체 구조를 정의</li>
<li>이 필드들을 초기화시키기 위해 생성자 정의</li>
</ul>
<h4 id="●-linkedlist-클래스">● LinkedList 클래스</h4>
<ul>
<li>실제 연결 리스트를 정의</li>
<li>head 필드 : private, ListNode 타입으로 선언, 연결 리스트의 첫 번째 노드를 가리킴</li>
</ul>
<h4 id="●-linkedlist-생성의-예">● LinkedList 생성의 예</h4>
<ul>
<li><p>LinkedList L = new LinkedList();
<img src="https://velog.velcdn.com/images/choish_dev/post/a54a7b8a-b7d8-4b76-a9a4-97b6fbbb90cf/image.png" alt=""></p>
</li>
<li><p>L에 노드가 연결되어 있는 경우
<img src="https://velog.velcdn.com/images/choish_dev/post/59c01876-e018-423c-bb16-46ced6e6cb14/image.png" alt=""></p>
</li>
</ul>
<h4 id="◆-단순-연결-리스트의-처리">◆ 단순 연결 리스트의 처리</h4>
<p><img src="https://velog.velcdn.com/images/choish_dev/post/5051c66a-512a-4031-a828-2a8e81bd8bcb/image.png" alt="">
<img src="https://velog.velcdn.com/images/choish_dev/post/bacc59f9-2212-40a8-9bc0-e83112bf4e88/image.png" alt="">
<img src="https://velog.velcdn.com/images/choish_dev/post/1d1e7adf-48e1-4df5-868f-9fc6fbc71949/image.png" alt="">
<img src="https://velog.velcdn.com/images/choish_dev/post/39cc3e70-c956-4e95-a94a-f4336cb3cda4/image.png" alt="">
<img src="https://velog.velcdn.com/images/choish_dev/post/ef37cb61-8884-49db-b99d-8a3669379f0b/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git에서 잘못된 Commit/PUSH 되돌리기 (개인 브랜치 기준)]]></title>
            <link>https://velog.io/@choish_dev/Git%EC%97%90%EC%84%9C-%EC%9E%98%EB%AA%BB%EB%90%9C-CommitPUSH-%EB%90%98%EB%8F%8C%EB%A6%AC%EA%B8%B0-%EA%B0%9C%EC%9D%B8-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EA%B8%B0%EC%A4%80</link>
            <guid>https://velog.io/@choish_dev/Git%EC%97%90%EC%84%9C-%EC%9E%98%EB%AA%BB%EB%90%9C-CommitPUSH-%EB%90%98%EB%8F%8C%EB%A6%AC%EA%B8%B0-%EA%B0%9C%EC%9D%B8-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EA%B8%B0%EC%A4%80</guid>
            <pubDate>Tue, 11 Nov 2025 07:24:45 GMT</pubDate>
            <description><![CDATA[<p>작업 중 git push를 한 후에 commit을 잘못해서 다른 파일까지 수정되거나 이름이 바뀌는 문제가 생길 수 있다.
이때 아래 단계를 차근차근 따라 하면 깔끔하게 복구할 수 있다.</p>
<blockquote>
<p> git에 하나의 폴더만 commit하려 했는데 다른 폴더들까지 같이 올라간 상황</p>
</blockquote>
<p>또는</p>
<blockquote>
<p>commit 메시지는 하나의 폴더에 관련한 내용인데 실제로 다른 파일까지 push된 상황</p>
</blockquote>
<hr>
<h3 id="⚙️-해결-방법">⚙️ 해결 방법</h3>
<h4 id="1-최근-commit만-취소하고-수정사항은-유지">1. 최근 commit만 취소하고, 수정사항은 유지</h4>
<pre><code class="language-bash">git reset --soft HEAD~1</code></pre>
<ul>
<li><p>최근 1개의 commit을 되돌림</p>
</li>
<li><p>수정한 파일들은 그대로 유지됨</p>
</li>
<li><p>즉, “commit만 취소” 상태</p>
</li>
</ul>
<h4 id="2-ds_08만-선택해서-다시-commit">2. DS_08만 선택해서 다시 commit</h4>
<pre><code class="language-bash">git reset        # 모든 파일을 unstaged 상태로 되돌림
git add &#39;폴더&#39;    # 업로드를 원하는 폴더만 staging
git commit -m &quot;DS_08 문제 해결&quot;</code></pre>
<h4 id="4-github에-강제-반영-push--f">4. GitHub에 강제 반영 (push -f)</h4>
<pre><code class="language-bash">git push -f</code></pre>
<blockquote>
<p>⚠️ -f(force)는 원격 저장소의 기록을 덮어쓰므로,
혼자 작업할 때만 사용하는 게 안전하다.</p>
</blockquote>
<hr>
<h3 id="정리-reset-옵션의-차이">정리: reset 옵션의 차이</h3>
<table>
<thead>
<tr>
<th>옵션</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>--soft</code></td>
<td>commit만 취소, 변경 내용은 staging 상태 유지</td>
<td>✅ commit만 지우고 수정은 남김</td>
</tr>
<tr>
<td><code>--mixed</code></td>
<td>commit과 staging 취소, 파일은 수정 상태 유지</td>
<td>🟨 git status에서 “modified”로 남음</td>
</tr>
<tr>
<td><code>--hard</code></td>
<td>commit + 수정 내용 모두 제거</td>
<td>❌ 되돌릴 수 없음 (주의!)</td>
</tr>
</tbody></table>
<hr>
<h3 id="결론">결론</h3>
<ul>
<li><p>실수로 잘못 commit/push해도 git reset --soft HEAD~1 → 필요한 파일만 add → git push -f 순서로 해결 가능하다.</p>
</li>
<li><p>특히 혼자 작업 중이라면 force push도 문제없지만, 협업 중이라면 <strong>git revert</strong>를 사용하는 게 안전하다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[IntelliJ와 GitHub 연동하기]]></title>
            <link>https://velog.io/@choish_dev/IntelliJ%EC%99%80-GitHub-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@choish_dev/IntelliJ%EC%99%80-GitHub-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 16 Oct 2025 05:15:31 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 진행하다 보면 버전 관리를 위해 GitHub와 연동하는 경우가 많습니다.
저 역시 IntelliJ에서 작업하던 프로젝트를 GitHub로 올려 관리하고 싶었습니다.
이번 글에서는 IntelliJ에서 GitHub와 연동하는 과정을 처음부터 단계별로 정리해보았습니다.</p>
<h2 id="1-github-준비">1. GitHub 준비</h2>
<ol>
<li>GitHub 계정 로그인</li>
<li>새 저장소 만들기 → <a href="https://github.com/new">https://github.com/new</a></li>
<li>저장소 이름 입력</li>
<li><strong>“Initialize with README”</strong> 체크 해제!</li>
<li><strong>“Create Repository”</strong> 클릭</li>
</ol>
<blockquote>
<p>→ 저장소 주소 예:
<a href="https://github.com/%EC%82%AC%EC%9A%A9%EC%9E%90%EB%AA%85/&quot;%EC%A0%80%EC%9E%A5%EC%86%8C%EC%9D%B4%EB%A6%84&quot;.git">https://github.com/사용자명/&quot;저장소이름&quot;.git</a></p>
</blockquote>
<hr>
<h2 id="2-intellij에서-git-활성화">2. IntelliJ에서 Git 활성화</h2>
<ol>
<li><p>File → Open → D:\자료구조 선택</p>
</li>
<li><p>상단 메뉴 → VCS → Enable Version Control Integration...
<img src="https://velog.velcdn.com/images/choish_dev/post/6510a5d0-b55a-497f-a4b8-f13db77f7865/image.png" alt=""></p>
</li>
<li><p>Git 선택 → OK
<img src="https://velog.velcdn.com/images/choish_dev/post/43071d9a-05f2-4402-b3da-d91fdc10ffde/image.png" alt=""></p>
</li>
<li><p>프로젝트 루트에 .git 폴더가 생기면 성공</p>
</li>
</ol>
<blockquote>
<p>터미널에서 확인:</p>
</blockquote>
<pre><code class="language-bash">git status</code></pre>
<hr>
<h2 id="3-gitignore-설정-필수">3. .gitignore 설정 (필수)</h2>
<blockquote>
<p>불필요한 파일이 올라가지 않도록 필터 역할을 합니다</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/choish_dev/post/3e57d254-9aca-4367-acb6-8a1949bd824a/image.png" alt=""></p>
<p>📄 프로젝트 루트에 .gitignore 생성 후 아래 내용 추가:</p>
<pre><code class="language-bash"># IntelliJ 설정
.idea/
*.iml
out/

# Eclipse (겸용)
bin/

# Java 빌드 결과물
*.class

# OS 자동 생성 파일
.DS_Store
Thumbs.db

# 기타
*.zip
*.log</code></pre>
<hr>
<h2 id="4-github-연결-최초-1회만">4. GitHub 연결 (최초 1회만)</h2>
<p>하단 Terminal 탭에서 아래 명령어 입력</p>
<pre><code class="language-bash">git branch -M main
git remote add origin https://github.com/&lt;계정명&gt;/&quot;저장소이름&quot;.git</code></pre>
<hr>
<h2 id="5-터미널로-커밋--푸시">5. 터미널로 커밋 &amp; 푸시</h2>
<ul>
<li><p>변경 확인</p>
<pre><code class="language-bash">git status</code></pre>
</li>
<li><p>변경 파일 추가</p>
<pre><code class="language-bash">git add .</code></pre>
</li>
<li><p>커밋 메시지 작성</p>
<pre><code class="language-bash">git commit -m &quot;내용&quot;</code></pre>
</li>
<li><p>GitHub에 업로드</p>
<pre><code class="language-bash">git push -u origin main //main branch에 업로드</code></pre>
</li>
</ul>
<blockquote>
<p>이후에는 git push만 써도 됩니다</p>
</blockquote>
<hr>
<h2 id="6-이후-반복-루틴">6. 이후 반복 루틴</h2>
<p>새 파일 추가나 수정 후엔 이 3줄만 입력하면 됩니다!!!</p>
<pre><code class="language-bash">git add .
git commit -m &quot;내용&quot;
git push</code></pre>
<p>※ 커밋 메시지 규칙 정리</p>
<table>
<thead>
<tr>
<th align="center">타입</th>
<th align="left">의미</th>
<th align="left">예시</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>feat</strong></td>
<td align="left">새로운 기능 추가</td>
<td align="left"><code>feat: 큐 기능 구현</code></td>
</tr>
<tr>
<td align="center"><strong>fix</strong></td>
<td align="left">버그 수정</td>
<td align="left"><code>fix: pop() 인덱스 오류 수정</code></td>
</tr>
<tr>
<td align="center"><strong>docs</strong></td>
<td align="left">문서 수정</td>
<td align="left"><code>docs: README 업데이트</code></td>
</tr>
<tr>
<td align="center"><strong>chore</strong></td>
<td align="left">설정 변경</td>
<td align="left"><code>chore: .gitignore 수정</code></td>
</tr>
</tbody></table>
<hr>
<h2 id="7-커밋-로그-확인">7. 커밋 로그 확인</h2>
<p>git log --oneline</p>
<p>결과 예시:</p>
<pre><code class="language-bash">b1a2c3d feat: 큐 구현 코드 추가
d2e3f4a feat: 스택 기능 완성</code></pre>
<hr>
<h2 id="💡-자주-쓰는-git-명령-요약표">💡 자주 쓰는 Git 명령 요약표</h2>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>git status</code></td>
<td>변경 내용 확인</td>
</tr>
<tr>
<td><code>git add .</code></td>
<td>모든 변경 파일 스테이징</td>
</tr>
<tr>
<td><code>git commit -m &quot;메시지&quot;</code></td>
<td>커밋 생성</td>
</tr>
<tr>
<td><code>git push</code></td>
<td>GitHub 업로드</td>
</tr>
<tr>
<td><code>git pull</code></td>
<td>최신 변경 내려받기</td>
</tr>
<tr>
<td><code>git log --oneline</code></td>
<td>커밋 히스토리 요약 보기</td>
</tr>
</tbody></table>
<hr>
<h2 id="📗-최종-요약">📗 최종 요약</h2>
<ol>
<li>VCS → Enable Version Control → Git</li>
<li>.gitignore 생성</li>
<li>git add .</li>
<li>git commit -m &quot;메시지&quot;</li>
<li>git push</li>
</ol>
<blockquote>
<p>IntelliJ와 Git을 연동해두면,
앞으로의 프로젝트 관리가 훨씬 깔끔해집니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS EC2 인스턴스 생성 시 보안그룹 동작 원리]]></title>
            <link>https://velog.io/@choish_dev/AWS-EC2-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1-%EC%8B%9C-%EB%B3%B4%EC%95%88%EA%B7%B8%EB%A3%B9-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC</link>
            <guid>https://velog.io/@choish_dev/AWS-EC2-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1-%EC%8B%9C-%EB%B3%B4%EC%95%88%EA%B7%B8%EB%A3%B9-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC</guid>
            <pubDate>Sun, 12 Oct 2025 07:47:22 GMT</pubDate>
            <description><![CDATA[<p>AWS에서 EC2 인스턴스를 처음 생성할 때,
항상 따라오는 개념이 바로 <strong>보안그룹(Security Group)</strong> 입니다.
하지만 많은 사람들이 헷갈리는 부분이 있죠.</p>
<blockquote>
<p>“default 보안그룹이 있는데, 왜 내가 건드린 launch-wizard-1만 적용될까?”</p>
</blockquote>
<p>그리고 AWS에서 EC2를 처음 배포할 때 가장 많이 겪는 문제가 바로 이겁니다.</p>
<blockquote>
<p>“Nginx까지 설치했는데 http://&lt;EC2 퍼블릭 IP&gt; 접속이 안 돼요!”</p>
</blockquote>
<p>저도 이번에 웹서버 구축 프로젝트를 EC2에 직접 배포하면서
이 문제를 정확히 겪었고, 결국 원인과 구조를 이해하게 됐습니다.</p>
<p>이 글에서는
<strong>✅ 보안그룹의 기본 원리
✅ default vs launch-wizard 차이
✅ 제가 겪은 실제 트러블슈팅 과정</strong>
위 3가지를 정리해보겠습니다. </p>
<br>

<h2 id="💡-1-보안그룹security-group이란">💡 1. 보안그룹(Security Group)이란?</h2>
<p>AWS의 보안그룹은 EC2 인스턴스 단위의 방화벽입니다.
즉, “이 인스턴스가 어떤 트래픽을 주고받을 수 있는가?”를 결정합니다.</p>
<ul>
<li><p>인바운드(Inbound) → 외부 → EC2 (예: 브라우저 접근, SSH 연결)</p>
</li>
<li><p>아웃바운드(Outbound) → EC2 → 외부 (예: apt install, API 요청 등)</p>
</li>
</ul>
<p>보안그룹이 허용하지 않은 트래픽은,
서버에서 Nginx가 잘 돌아가도 외부에서 절대 접근할 수 없습니다.</p>
<br>

<h2 id="2-ec2-생성-시-보안그룹-연결-원리">2. EC2 생성 시 보안그룹 연결 원리</h2>
<p>AWS는 EC2를 생성할 때 자동으로 하나의 보안그룹을 연결합니다.
이때 어떤 그룹이 연결되는지는 EC2 생성 시 옵션에 따라 달라집니다.</p>
<table>
<thead>
<tr>
<th>생성 시 선택 옵션</th>
<th>결과</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>“새 보안 그룹 생성(Create new security group)”</td>
<td><code>launch-wizard-1</code> 자동 생성</td>
<td>EC2 마법사에서 새 그룹이 자동 생성되어 연결됨</td>
</tr>
<tr>
<td>“기존 보안 그룹 선택(Select existing security group)” → <code>default</code> 선택</td>
<td><code>default</code> 그룹 사용</td>
<td>기존 default 그룹을 그대로 재사용</td>
</tr>
<tr>
<td>AMI 복제</td>
<td>원본의 보안그룹 그대로 유지</td>
<td>launch-wizard 또는 custom 그룹 그대로 복사</td>
</tr>
</tbody></table>
<br>

<h2 id="3-default-vs-launch-wizard-비교표">3. default vs launch-wizard 비교표</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>default 보안그룹</th>
<th>launch-wizard 보안그룹</th>
</tr>
</thead>
<tbody><tr>
<td>생성 시점</td>
<td>VPC 생성 시 자동 생성</td>
<td>EC2 생성 시 자동 생성</td>
</tr>
<tr>
<td>기본 규칙</td>
<td>내부 통신만 허용, 외부 차단</td>
<td>생성 시 선택한 포트(22, 80 등) 허용</td>
</tr>
<tr>
<td>인스턴스 연결 여부</td>
<td>❌ 기본적으로 연결 안 됨</td>
<td>✅ EC2 생성 시 자동 연결</td>
</tr>
<tr>
<td>주요 용도</td>
<td>내부 DB 통신, 내부 테스트용</td>
<td>실제 서비스용 (웹서버, API 서버 등)</td>
</tr>
<tr>
<td>이름 변경</td>
<td>불가능</td>
<td>가능 (예: <code>web-prod-sg</code>)</td>
</tr>
<tr>
<td>흔한 실수</td>
<td>default에 포트 열었는데 인스턴스는 launch-wizard에 연결됨</td>
<td>※ 가장 흔한 문제!</td>
</tr>
</tbody></table>
<br>

<h2 id="💥-4-실제-트러블슈팅-사례-nginx-접속-안-됨">💥 4. 실제 트러블슈팅 사례: “Nginx 접속 안 됨”</h2>
<h3 id="🧩-문제-상황">🧩 문제 상황</h3>
<p>AWS EC2 인스턴스에 Nginx를 설치하고
sudo systemctl status nginx로 확인해보면 active (running) 상태.
포트도 0.0.0.0:80으로 잘 열려 있음.</p>
<p>그런데도 브라우저에서 http://&lt;퍼블릭 IP&gt; 접속 시 아무 응답이 없음.</p>
<pre><code class="language-bash">ssh -i &quot;gangwonfocus-key.pem&quot; ubuntu@&lt;Public IP&gt;
curl http://localhost     # 정상 동작</code></pre>
<blockquote>
<p>즉, 서버 내부에서는 잘 뜨는데 외부에서는 접속 불가 상태였습니다.</p>
</blockquote>
<h3 id="🔍-원인-분석">🔍 원인 분석</h3>
<p>AWS 보안그룹 인바운드 규칙을 확인했는데,
HTTP(80), HTTPS(443), SSH(22)가 모두 허용돼 있었음.
그래서 처음엔 &quot;보안그룹 문제는 아니다&quot;라고 착각.</p>
<p>하지만 인스턴스 상세 탭을 다시 보니,
실제로 적용된 보안그룹 이름이 launch-wizard-1 이었습니다.
그런데 내가 설정했던 규칙은 default 보안그룹이었습니다.</p>
<blockquote>
<p>즉, 규칙은 default에 추가했지만,
EC2는 launch-wizard-1을 사용 중이라서
실제로는 아무 트래픽도 허용되지 않았던 것!</p>
</blockquote>
<h3 id="✅-해결-방법">✅ 해결 방법</h3>
<ol>
<li><p>EC2 → 보안 탭 → launch-wizard-1 클릭
<img src="https://velog.velcdn.com/images/choish_dev/post/a4f62942-14fc-452d-9621-c33fc05ad28f/image.png" alt=""></p>
</li>
<li><p>인바운드 규칙에 다음 항목 추가</p>
<table>
<thead>
<tr>
<th>유형(Type)</th>
<th>프로토콜</th>
<th>포트</th>
<th>소스(Source)</th>
</tr>
</thead>
<tbody><tr>
<td>HTTP</td>
<td>TCP</td>
<td>80</td>
<td>0.0.0.0/0</td>
</tr>
<tr>
<td>HTTPS</td>
<td>TCP</td>
<td>443</td>
<td>0.0.0.0/0</td>
</tr>
<tr>
<td>SSH</td>
<td>TCP</td>
<td>22</td>
<td>0.0.0.0/0</td>
</tr>
</tbody></table>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/choish_dev/post/00546fb6-11a2-4370-a6f4-4de510583d9c/image.png" alt=""></p>
<ol start="3">
<li>저장 후 브라우저에서 다시 접속
“Welcome to nginx!” 
<img src="https://velog.velcdn.com/images/choish_dev/post/5e8c714f-b401-41a1-98ec-5f2fb5439daa/image.png" alt=""></li>
</ol>
<h3 id="💬-배운-점">💬 배운 점</h3>
<ul>
<li><p>EC2 인스턴스가 어떤 보안그룹에 연결되어 있는지 항상 확인할 것.</p>
</li>
<li><p>규칙을 잘못된 그룹(default 등)에 추가해봤자 아무 효과 없음.</p>
</li>
<li><p>보안그룹 이름이 launch-wizard-1, launch-wizard-2 처럼 자동 생성될 수 있으니 의미 있는 이름으로 바꿔두면 헷갈리지 않는다.</p>
</li>
</ul>
<br>

<h2 id="5-보안그룹-관리-팁">5. 보안그룹 관리 팁</h2>
<p> 보안그룹 이름은 서비스별로 명확하게 바꿔주자!
예시:</p>
<table>
<thead>
<tr>
<th>이전 이름</th>
<th>새 이름</th>
<th>용도</th>
</tr>
</thead>
<tbody><tr>
<td>launch-wizard-1</td>
<td>webserver-sg</td>
<td>React + Nginx 서버용</td>
</tr>
<tr>
<td>launch-wizard-2</td>
<td>springboot-api-sg</td>
<td>Spring Boot API 서버용</td>
</tr>
<tr>
<td>launch-wizard-3</td>
<td>rds-db-sg</td>
<td>MySQL 접근 전용</td>
</tr>
</tbody></table>
<blockquote>
<p>이름만 봐도 역할이 드러나게 하면,
나중에 CI/CD, RDS 연결, HTTPS 설정할 때 훨씬 깔끔하게 관리됩니다.</p>
</blockquote>
<p> 결론</p>
<ul>
<li><p>AWS EC2 생성 시 자동 연결되는 보안그룹은 대부분 launch-wizard-#</p>
</li>
<li><p>default는 내부 통신용이며 외부 접근 불가</p>
</li>
<li><p>포트(80, 22, 443 등) 허용은 EC2가 실제로 연결된 보안그룹에 해야 함</p>
</li>
<li><p>보안그룹을 잘못 설정해도 AWS는 별다른 에러를 주지 않기 때문에,
접속 불가 문제 발생 시 항상 “인스턴스 → 보안 탭 → 실제 그룹 이름”부터 확인!</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[StringTokenizer란?(split()과 차이점)]]></title>
            <link>https://velog.io/@choish_dev/StringTokenizer%EB%9E%80</link>
            <guid>https://velog.io/@choish_dev/StringTokenizer%EB%9E%80</guid>
            <pubDate>Fri, 12 Sep 2025 09:08:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>StringTokenizer</strong>은 Java에서 문자열을 특정 구분자(delimiter)를 기준으로 잘라주는 클래스입니다.
split() 메서드와 비슷하지만, 더 가볍고 빠른 동작을 위해 만들어졌습니다.</p>
</blockquote>
<h3 id="🔹-기본-사용법">🔹 기본 사용법</h3>
<pre><code class="language-java">import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) {
        String str = &quot;apple,banana,orange&quot;;

        // 구분자: &quot;,&quot;
        StringTokenizer st = new StringTokenizer(str, &quot;,&quot;);

        while (st.hasMoreTokens()) {
            System.out.println(st.nextToken());
        }
    }
}
</code></pre>
<p><strong>출력</strong></p>
<blockquote>
<p>apple
banana
orange</p>
</blockquote>
<br>

<h3 id="🔹-주요-메서드">🔹 주요 메서드</h3>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>hasMoreTokens()</code></td>
<td>다음 토큰이 존재하면 true 반환</td>
</tr>
<tr>
<td><code>nextToken()</code></td>
<td>다음 토큰을 반환</td>
</tr>
<tr>
<td><code>countTokens()</code></td>
<td>남아있는 토큰 개수를 반환</td>
</tr>
</tbody></table>
<br>

<h3 id="🔹-split과의-차이점">🔹 split()과의 차이점</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>StringTokenizer</th>
<th>split()</th>
</tr>
</thead>
<tbody><tr>
<td>리턴 타입</td>
<td>토큰을 하나씩 꺼내 씀</td>
<td>문자열 배열(String[]) 반환</td>
</tr>
<tr>
<td>성능</td>
<td>가볍고 빠름</td>
<td>상대적으로 무거움</td>
</tr>
<tr>
<td>정규식 지원</td>
<td>❌ 불가능</td>
<td>✅ 가능 (정규식 기반 분리)</td>
</tr>
<tr>
<td>사용 편의성</td>
<td>순차 접근</td>
<td>랜덤 접근 가능</td>
</tr>
</tbody></table>
<br>

<h3 id="🔹-활용-예시-코딩테스트">🔹 활용 예시 (코딩테스트)</h3>
<pre><code class="language-java">import java.io.*;
import java.util.StringTokenizer;

public class FastInput {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int T = Integer.parseInt(br.readLine());

        for (int i = 0; i &lt; T; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine(), &quot; &quot;);
            int A = Integer.parseInt(st.nextToken());
            int B = Integer.parseInt(st.nextToken());
            System.out.println(A + B);
        }
    }
}
</code></pre>
<p><strong>장점</strong></p>
<ul>
<li><p>입력값이 공백 단위로 구분될 때 빠르게 처리 가능</p>
</li>
<li><p>별도의 배열 선언 없이 순차적으로 값 꺼내기 가능</p>
</li>
</ul>
<br>

<h3 id="🔹-언제-쓰면-좋을까">🔹 언제 쓰면 좋을까?</h3>
<ul>
<li><p>입력 데이터가 공백 또는 단순 구분자로 구분될 때</p>
</li>
<li><p>빠르고 가벼운 파싱이 필요할 때</p>
</li>
<li><p>코딩테스트에서 split() 대신 빠른 성능이 필요할 때</p>
</li>
</ul>
<br>

<h3 id="📗정리">📗정리</h3>
<ul>
<li><p>StringTokenizer는 문자열을 구분자 단위로 잘라주는 클래스</p>
</li>
<li><p>split()과 다르게 순차 접근만 가능하지만, 속도가 빠름</p>
</li>
<li><p>코딩테스트에서 입력 처리 최적화할 때 자주 사용</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 코딩테스트 입력 도구 BufferedReader란 무엇인가?]]></title>
            <link>https://velog.io/@choish_dev/%EC%9E%90%EB%B0%94-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%85%EB%A0%A5-%EB%8F%84%EA%B5%AC-BufferedReader%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@choish_dev/%EC%9E%90%EB%B0%94-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%85%EB%A0%A5-%EB%8F%84%EA%B5%AC-BufferedReader%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Thu, 04 Sep 2025 07:09:32 GMT</pubDate>
            <description><![CDATA[<h3 id="📌-개요">📌 개요</h3>
<p>코딩테스트에서 입력 속도가 느려서 시간 초과를 겪은 적 있나요?
자바의 기본 입력 도구인 Scanner는 편리하지만 속도가 느려서 대량 입력에는 불리합니다.
이때 사용하는 것이 바로 <strong>BufferedReader</strong>입니다.</p>
<br>

<h3 id="🔍-bufferedreader란">🔍 BufferedReader란?</h3>
<ul>
<li><p>자바에서 문자 기반 입력을 버퍼링하여 효율적으로 읽어들이는 클래스</p>
</li>
<li><p>InputStreamReader와 함께 사용하여 System.in(표준 입력)을 읽음</p>
</li>
<li><p>코딩테스트에서는 빠른 입력 처리를 위해 사실상 필수</p>
<blockquote>
<p>BufferedReader는 문자 입력 스트림을 버퍼링(buffering)해서 읽는 클래스입니다.
즉, 한 글자씩 읽는 대신 <strong>버퍼(임시 메모리 공간)</strong>를 이용해 대량으로 읽어와서 성능을 높여줍니다.
보통 InputStreamReader와 함께 사용하여 콘솔 입력(System.in)을 효율적으로 읽습니다.</p>
</blockquote>
</li>
</ul>
<br>

<h3 id="🛠️-기본-구조">🛠️ 기본 구조</h3>
<pre><code class="language-java">BufferedReader br = new BufferedReader(new InputStreamReader(System.in));</code></pre>
<br>

<h3 id="🧩-주요-메서드">🧩 주요 메서드</h3>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
<th>반환 타입</th>
<th>사용 예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>readLine()</code></td>
<td>한 줄 전체 읽기 (개행 문자 제외)</td>
<td><code>String</code></td>
<td><code>String s = br.readLine();</code></td>
</tr>
<tr>
<td><code>read()</code></td>
<td>한 글자 읽기 (없으면 -1 반환)</td>
<td><code>int</code></td>
<td><code>int c = br.read();</code></td>
</tr>
<tr>
<td><code>ready()</code></td>
<td>읽을 문자가 준비되어 있는지 확인</td>
<td><code>boolean</code></td>
<td><code>if (br.ready()) {...}</code></td>
</tr>
<tr>
<td><code>skip(long n)</code></td>
<td>n개의 문자 건너뛰기</td>
<td><code>long</code></td>
<td><code>br.skip(2);</code></td>
</tr>
<tr>
<td><code>close()</code></td>
<td>스트림 닫기 (자원 해제)</td>
<td><code>void</code></td>
<td><code>br.close();</code></td>
</tr>
</tbody></table>
<br>

<h3 id="✨-사용-예시">✨ 사용 예시</h3>
<h4 id="🔹-한-줄-입력-받기">🔹 한 줄 입력 받기</h4>
<pre><code class="language-java">import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String input = br.readLine(); // 한 줄 입력
        System.out.println(&quot;입력값: &quot; + input);
    }
}</code></pre>
<br>

<h4 id="🔹-여러-정수-입력-받기">🔹 여러 정수 입력 받기</h4>
<pre><code class="language-java">String[] numbers = br.readLine().split(&quot; &quot;);
int a = Integer.parseInt(numbers[0]);
int b = Integer.parseInt(numbers[1]);

System.out.println(a + b);</code></pre>
<br>

<h3 id="⚡-bufferedreader-vs-scanner">⚡ BufferedReader vs Scanner</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>BufferedReader</th>
<th>Scanner</th>
</tr>
</thead>
<tbody><tr>
<td>속도</td>
<td>빠름 (버퍼 사용)</td>
<td>상대적으로 느림</td>
</tr>
<tr>
<td>입력 단위</td>
<td>문자열(String)</td>
<td>다양한 타입 지원</td>
</tr>
<tr>
<td>변환</td>
<td>직접 파싱 필요 (<code>Integer.parseInt</code>)</td>
<td>자동 파싱 가능 (<code>nextInt</code>)</td>
</tr>
<tr>
<td>코딩테스트 적합성</td>
<td>✅ 추천</td>
<td>❌ 입력 많으면 시간 초과 위험</td>
</tr>
</tbody></table>
<br>

<h3 id="🚀-정리">🚀 정리</h3>
<ul>
<li><p>BufferedReader = 빠른 입력 처리용 클래스</p>
</li>
<li><p>readLine()으로 문자열 입력 → 필요 시 split()과 Integer.parseInt()로 변환</p>
</li>
<li><p>flush()는 입력이 아니라 출력 스트림에서만 사용</p>
</li>
<li><p>코딩테스트에서는 Scanner 대신 BufferedReader + StringBuilder/BufferedWriter 조합이 정석</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[🔒 JWT란?]]></title>
            <link>https://velog.io/@choish_dev/JWT%EB%9E%80</link>
            <guid>https://velog.io/@choish_dev/JWT%EB%9E%80</guid>
            <pubDate>Sat, 23 Aug 2025 09:56:26 GMT</pubDate>
            <description><![CDATA[<p>웹 서비스를 만들다 보면 가장 먼저 부딪히는 것이 “사용자 인증” 문제입니다.
세션/쿠키 방식부터 OAuth, 토큰 기반 인증까지 다양한 방법이 존재하는데, 그중 가장 많이 쓰이는 것이 <strong>JWT(Json Web Token)</strong> 입니다.</p>
<p>이번 글에서는 JWT가 무엇이고, 왜 필요한지, 어떻게 사용하는지 정리해보겠습니다.</p>
<hr>
<h2 id="📌jwtjson-web-toekn-정의">📌JWT(Json Web Toekn) 정의</h2>
<p>JWT(Json Web Token) 는 웹 표준(RFC 7519)으로 정의된 인증 및 정보 교환을 위한 토큰 형식입니다.</p>
<ul>
<li><p>JSON 포맷으로 데이터를 담습니다.</p>
</li>
<li><p>전자서명(Signature) 을 포함해 위·변조를 방지합니다.</p>
</li>
<li><p>서버가 세션을 직접 저장하지 않아도 되므로 Stateless 인증 방식에 활용됩니다.</p>
</li>
</ul>
<p>즉, 클라이언트가 JWT를 가지고 있으면 서버는 추가 DB 조회나 세션 저장소 없이도 사용자의 신원을 확인할 수 있습니다.</p>
<h3 id="1-jwt-구조">1. JWT 구조</h3>
<p>JWT는 Header.Payload.Signature 형태의 세 부분으로 구성됩니다.</p>
<pre><code>xxxxx.yyyyy.zzzzz</code></pre><p><img src="https://velog.velcdn.com/images/choish_dev/post/ff12c915-37ba-4153-93ce-b39831c161a7/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>구성 요소</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Header</strong></td>
<td>알고리즘 정보(HS256, RS256 등) + 타입(JWT)</td>
</tr>
<tr>
<td><strong>Payload</strong></td>
<td>사용자 정보(예: id, email, role) + 만료 시간(exp)</td>
</tr>
<tr>
<td><strong>Signature</strong></td>
<td>Header + Payload를 비밀키로 서명하여 변조 방지</td>
</tr>
<tr>
<td>예시 (디코딩 후):</td>
<td></td>
</tr>
<tr>
<td>```json</td>
<td></td>
</tr>
<tr>
<td>{</td>
<td></td>
</tr>
<tr>
<td>&quot;alg&quot;: &quot;HS256&quot;,</td>
<td></td>
</tr>
<tr>
<td>&quot;typ&quot;: &quot;JWT&quot;</td>
<td></td>
</tr>
<tr>
<td>}</td>
<td></td>
</tr>
<tr>
<td>{</td>
<td></td>
</tr>
<tr>
<td>&quot;id&quot;: 1,</td>
<td></td>
</tr>
<tr>
<td>&quot;username&quot;: &quot;seunghyeok&quot;,</td>
<td></td>
</tr>
<tr>
<td>&quot;exp&quot;: 1692897200</td>
<td></td>
</tr>
<tr>
<td>}</td>
<td></td>
</tr>
</tbody></table>
<pre><code>
### 2. JWT 동작 과정
1. 로그인 요청 → 사용자가 ID/PW를 서버에 전달

2. 토큰 발급 → 서버가 사용자 정보를 확인 후 JWT 생성, 클라이언트에 전달

3. 인증 요청 → 클라이언트가 API 요청 시 Authorization: Bearer &lt;JWT&gt; 헤더에 담아 전달

4. 서버 검증 → 서버는 비밀키로 JWT 서명(Signature) 검증 후 권한 체크

5. 응답 → 정상 요청이면 데이터 반환

👉 세션/쿠키 기반 인증과 달리 서버가 상태(state)를 저장하지 않기 때문에 Stateless 인증이라고 부릅니다.

### 3. JWT의 장점

- Stateless: 서버에 세션 저장소가 필요 없음

- 확장성: 마이크로서비스, 다른 도메인 간 인증에 용이

- 보편성: OAuth2, OpenID Connect 등 다양한 인증 표준에서 활용

### 4. JWT의 단점

- 토큰 크기: Payload에 많은 정보를 넣으면 크기가 커짐 → 네트워크 부담

- 무효화 어려움: 발급된 토큰은 만료 전까지 유효 → 강제 로그아웃 처리 어려움

- 보안 취약성: 토큰 탈취 시 그대로 사용 가능 → 반드시 HTTPS 필요
&gt;  💡 보안 고려사항
  - 토큰 저장 위치
      - localStorage: 간단하지만 XSS 공격에 취약
    - HttpOnly Cookie: 보안성 ↑, 하지만 CORS 설정 필요
  - 토큰 만료 시간 설정
    - Access Token 짧게, Refresh Token 길게 → 재발급 전략 필요
  - HTTPS 필수
    - 평문 전송 시 토큰 탈취 위험


### 5. JWT의 활용 예시

- 웹/모바일 로그인 인증

- OAuth2 기반 SNS 로그인 (구글, 네이버, 카카오)

- API 서버 간 통신 인증 (Microservice 환경)

- Refresh Token 전략으로 장기 세션 유지

  &lt;br&gt;

## 📗정리
&gt; JWT는 현대 웹 서비스에서 인증의 핵심 요소입니다.
하지만 만능은 아니며, 적절한 만료 정책, 저장 전략, Refresh Token 운용 등을 함께 고려해야 안전한 인증 시스템을 만들 수 있습니다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[React vs JSP 차이]]></title>
            <link>https://velog.io/@choish_dev/React-vs-JSP-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@choish_dev/React-vs-JSP-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Thu, 21 Aug 2025 07:44:18 GMT</pubDate>
            <description><![CDATA[<p>웹 개발을 하다 보면 서버 사이드 렌더링(SSR) 과 클라이언트 사이드 렌더링(CSR) 이라는 두 가지 접근 방식을 접하게 됩니다.
대표적으로 Java 진영에서 많이 사용되는 JSP(Java Server Pages) 는 서버 사이드 렌더링 기술이고, 최신 프론트엔드 개발에서 널리 사용되는 React 는 클라이언트 사이드 렌더링을 기반으로 합니다.
두 기술은 모두 “사용자에게 동적인 웹 페이지를 제공한다”는 목표는 같지만, 렌더링 방식과 개발 패러다임에서 큰 차이가 있습니다.
이번 글에서는 React와 JSP의 차이점을 정리해 보겠습니다.</p>
<hr>
<h3 id="1-기본-개념">1. 기본 개념</h3>
<h4 id="📌-jsp-java-server-pages">📌 JSP (Java Server Pages)</h4>
<ul>
<li><p>서버 사이드 렌더링(SSR) 기술.</p>
</li>
<li><p>HTML 코드 안에 Java 코드를 삽입하여 동적으로 페이지를 생성.</p>
</li>
<li><p>요청이 올 때마다 서버에서 JSP를 컴파일/실행하여 HTML을 생성한 뒤 브라우저에 전달.</p>
</li>
</ul>
<h4 id="📌-react">📌 React</h4>
<ul>
<li><p>클라이언트 사이드 렌더링(CSR) 기반의 자바스크립트 라이브러리.</p>
</li>
<li><p>컴포넌트 단위로 UI를 구성하며, 데이터 변화에 따라 브라우저에서 즉각적으로 UI 갱신.</p>
</li>
<li><p>SPA(Single Page Application) 개발에 특화.</p>
</li>
</ul>
<br>

<h3 id="2-렌더링-방식">2. 렌더링 방식</h3>
<h4 id="📌-jsp-java-server-pages-1">📌 JSP (Java Server Pages)</h4>
<p>요청(Request) → 서버에서 JSP 처리 → HTML 완성 → 브라우저에 전달.
<strong>(새로운 페이지 요청마다 서버와 통신)</strong></p>
<h4 id="📌-react-1">📌 React</h4>
<p>최초에 빈 HTML + JS 번들 로드 → 이후에는 브라우저에서 Virtual DOM을 이용해 필요한 부분만 갱신.
<strong>(빠른 UI 업데이트, 서버 요청 최소화)</strong></p>
<br>

<h3 id="3-개발-패러다임">3. 개발 패러다임</h3>
<h4 id="📌-jsp-java-server-pages-2">📌 JSP (Java Server Pages)</h4>
<ul>
<li><p>전통적인 MVC 패턴에서 View 역할을 담당.</p>
</li>
<li><p>Java 코드와 HTML이 섞여서 유지보수가 어려워질 수 있음.</p>
</li>
<li><p>** 서버 중심 개발.**</p>
</li>
</ul>
<h4 id="📌-react-2">📌 React</h4>
<ul>
<li><p>컴포넌트 기반 개발: UI를 재사용 가능한 작은 단위로 분리.</p>
</li>
<li><p>상태 관리(State, Props)를 통한 동적 렌더링.</p>
</li>
<li><p>** 클라이언트 중심 개발.**</p>
</li>
</ul>
<br>

<h3 id="4-성능-및-사용자-경험">4. 성능 및 사용자 경험</h3>
<h4 id="📌-jsp-java-server-pages-3">📌 JSP (Java Server Pages)</h4>
<ul>
<li><p>매 요청마다 페이지 전체 새로고침 → UX가 무겁고 느리게 느껴질 수 있음.</p>
</li>
<li><p>초기 로딩은 빠르지만, 자주 이동하는 경우 비효율적.</p>
</li>
</ul>
<h4 id="📌-react-3">📌 React</h4>
<ul>
<li><p>초기 로딩 시 JS 번들 크기 때문에 무거울 수 있음.</p>
</li>
<li><p>하지만 이후엔 부분 갱신(빠른 UI 반응) 덕분에 UX가 우수.</p>
</li>
</ul>
<br>

<h3 id="5-활용-사례">5. 활용 사례</h3>
<h4 id="📌-jsp-java-server-pages-4">📌 JSP (Java Server Pages)</h4>
<ul>
<li><p>예전 기업용 웹 애플리케이션, 관리자 페이지 등.</p>
</li>
<li><p>서버 렌더링 기반이라 <strong>SEO(검색 엔진 최적화)</strong>에는 유리.</p>
</li>
</ul>
<h4 id="📌-react-4">📌 React</h4>
<ul>
<li><p>최신 웹 서비스(네이버, 카카오, 넷플릭스 등).</p>
</li>
<li><p>사용자와 상호작용이 많은 SPA 개발에 적합.</p>
</li>
</ul>
<br>

<h3 id="📄-react-vs-jsp-비교-표">📄 React vs JSP 비교 표</h3>
<style>
  table.compare-table {
    width: 100%;
    border-collapse: collapse;
    text-align: center;
  }
  .compare-table th, .compare-table td {
    border: 1px solid #ccc;   /* ✅ 세로줄 + 가로줄 표시 */
    padding: 10px;
  }
  .compare-table th {
    background-color: #f8f8f8; /* ✅ 헤더 배경색 */
  }
</style>

<table class="compare-table">
  <thead>
    <tr>
      <th>구분</th>
      <th>JSP (Java Server Pages)</th>
      <th>React</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><b>렌더링 방식</b></td>
      <td>서버 사이드 렌더링 (SSR)<br>요청 시 서버에서 HTML 생성</td>
      <td>클라이언트 사이드 렌더링 (CSR)<br>Virtual DOM으로 브라우저에서 UI 갱신</td>
    </tr>
    <tr>
      <td><b>동작 원리</b></td>
      <td>요청 → 서버에서 JSP 실행 → HTML 완성 후 브라우저 전달</td>
      <td>최초 로딩 시 JS 번들 로드 → 이후 브라우저에서 상태 변화에 따라 부분 갱신</td>
    </tr>
    <tr>
      <td><b>개발 패러다임</b></td>
      <td>전통적인 MVC 패턴의 View 역할<br>Java 코드와 HTML 혼합</td>
      <td>컴포넌트 기반 UI 개발<br>상태 관리(State, Props) 중심</td>
    </tr>
    <tr>
      <td><b>UX(사용자 경험)</b></td>
      <td>페이지 전체 새로고침 발생<br>이동 시 속도 느려질 수 있음</td>
      <td>빠른 화면 전환, 부분 갱신<br>SPA 구조로 UX 우수</td>
    </tr>
    <tr>
      <td><b>성능 특징</b></td>
      <td>초기 로딩 빠름<br>하지만 요청이 많으면 서버 부하 ↑</td>
      <td>초기 로딩 무거울 수 있음<br>이후에는 가볍고 반응 속도 ↑</td>
    </tr>
    <tr>
      <td><b>SEO(검색엔진 최적화)</b></td>
      <td>서버에서 HTML 완성 → SEO 친화적</td>
      <td>CSR이라 SEO 불리할 수 있음<br>(Next.js 같은 SSR 프레임워크로 보완)</td>
    </tr>
    <tr>
      <td><b>활용 사례</b></td>
      <td>전통적인 기업용 웹, 관리 시스템</td>
      <td>최신 웹 서비스, SPA, 대규모 프론트엔드 애플리케이션</td>
    </tr>
    <tr>
      <td><b>중심 구조</b></td>
      <td>서버 중심</td>
      <td>클라이언트 중심</td>
    </tr>
  </tbody>
</table>

<br>

<h3 id="📝-결론">📝 결론</h3>
<blockquote>
<p>JSP는 서버에서 완성된 HTML을 내려주는 <strong>서버 사이드 렌더링 방식</strong>이고, React는 브라우저에서 동적으로 화면을 그려주는 <strong>클라이언트 사이드 렌더링</strong> 방식입니다.
JSP는 전통적인 MVC 구조와 잘 맞고, React는 컴포넌트 기반 SPA 개발에 특화되어 있습니다.
즉, JSP는 서버 중심, React는 클라이언트 중심이라고 이해하면 쉽습니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Axios vs Fetch - 무엇을 사용해야 할까?]]></title>
            <link>https://velog.io/@choish_dev/Axios-vs-Fetch-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@choish_dev/Axios-vs-Fetch-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Thu, 21 Aug 2025 07:19:48 GMT</pubDate>
            <description><![CDATA[<h4 id="웹-개발을-하다-보면-서버와-데이터를-주고받기-위해-http-요청을-보내야-할-때가-많습니다">웹 개발을 하다 보면 서버와 데이터를 주고받기 위해 HTTP 요청을 보내야 할 때가 많습니다.</h4>
<h4 id="대표적으로-fetch-api와-axios가-많이-사용되는데-이-둘의-차이를-정리해보겠습니다">대표적으로 Fetch API와 Axios가 많이 사용되는데, 이 둘의 차이를 정리해보겠습니다.</h4>
<hr>
<h3 id="📌-axios란">📌 Axios란?</h3>
<ul>
<li>외부 라이브러리 (설치 필요: npm install axios)</li>
<li>Fetch보다 다양한 기능을 지원</li>
<li>JSON 자동 파싱, 에러 자동 처리, 요청/응답 인터셉터 등 실무에서 많이 사용됨</li>
</ul>
<p>👉 예시:</p>
<pre><code class="language-javascript">import axios from &quot;axios&quot;;

axios.get(&quot;/api/user&quot;)
  .then(response =&gt; {
    console.log(response.data);
  })
  .catch(error =&gt; {
    console.error(error);
  });</code></pre>
<h3 id="📌-fetch-api란">📌 Fetch API란?</h3>
<ul>
<li>브라우저에 기본 내장된 HTTP 요청 기능</li>
<li>추가 설치 없이 바로 사용 가능</li>
<li>ES6부터 공식적으로 제공됨</li>
</ul>
<p>👉 예시:</p>
<pre><code class="language-javascript">fetch(&quot;/api/user&quot;)
  .then(response =&gt; {
    if (!response.ok) {
      throw new Error(&quot;HTTP Error: &quot; + response.status);
    }
    return response.json();
  })
  .then(data =&gt; console.log(data))
  .catch(err =&gt; console.error(err));</code></pre>
<h3 id="🛠️-axios-vs-fetch-차이-정리">🛠️ Axios vs Fetch 차이 정리</h3>
<table border="1" cellspacing="0" cellpadding="8">
  <thead>
    <tr>
      <th>구분</th>
      <th>Fetch</th>
      <th>Axios</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>지원 여부</td>
      <td>브라우저 기본 제공</td>
      <td>별도 설치 필요</td>
    </tr>
    <tr>
      <td>응답 처리</td>
      <td><code>response.json()</code>으로 파싱 필요</td>
      <td>JSON 자동 파싱</td>
    </tr>
    <tr>
      <td>에러 처리</td>
      <td>HTTP 400/500도 "성공"으로 처리됨 → <code>response.ok</code> 직접 확인</td>
      <td>HTTP 에러를 자동으로 <code>catch</code>로 전달</td>
    </tr>
    <tr>
      <td>요청 취소</td>
      <td><code>AbortController</code> 사용해야 함</td>
      <td><code>CancelToken</code> 제공 (간단)</td>
    </tr>
    <tr>
      <td>기능 확장성</td>
      <td>단순 요청에 적합</td>
      <td>인터셉터, 헤더 자동 설정, 응답 변환 등 다양한 기능</td>
    </tr>
    <tr>
      <td>호환성</td>
      <td>최신 브라우저 중심, 구형 브라우저 지원 어려움</td>
      <td>IE 등 구형 브라우저 지원 가능</td>
    </tr>
  </tbody>
</table>

<p><br><br></p>
<h3 id="💡왜-axios를-더-많이-쓸까">💡왜 Axios를 더 많이 쓸까?</h3>
<ul>
<li>인터셉터(Interceptor): 요청 전/후 가로채서 공통 로직 적용 가능 (ex. JWT 토큰 자동 첨부)</li>
<li>에러 처리 간편: 400, 500 에러도 catch로 바로 감지 가능</li>
<li>브라우저 호환성: 다양한 브라우저 지원</li>
<li>코드 가독성: 짧고 직관적인 코드 작성 가능</li>
</ul>
<blockquote>
<p>🔍 정리하자면</p>
</blockquote>
<ul>
<li>간단한 요청 → Fetch로도 충분</li>
<li>실무/프로젝트 규모가 크다 → Axios 추천</li>
</ul>
<hr>
<h3 id="✍🏼-결론">✍🏼 결론:</h3>
<blockquote>
<p>&quot;Fetch는 가볍고 기본적인 요청에 적합하고, Axios는 실무에서 확장성과 편리성을 고려할 때 더 많이 사용된다.&quot;</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[서브모듈 문제와 push 거절 오류 해결기]]></title>
            <link>https://velog.io/@choish_dev/%EC%84%9C%EB%B8%8C%EB%AA%A8%EB%93%88-%EB%AC%B8%EC%A0%9C%EC%99%80-push-%EA%B1%B0%EC%A0%88-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0%EA%B8%B0</link>
            <guid>https://velog.io/@choish_dev/%EC%84%9C%EB%B8%8C%EB%AA%A8%EB%93%88-%EB%AC%B8%EC%A0%9C%EC%99%80-push-%EA%B1%B0%EC%A0%88-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0%EA%B8%B0</guid>
            <pubDate>Thu, 14 Aug 2025 08:12:37 GMT</pubDate>
            <description><![CDATA[<h3 id="1-상황">1. 상황</h3>
<p><img src="https://velog.velcdn.com/images/choish_dev/post/9b3ebfe0-6a2f-4d1b-868b-0d05dfdf708f/image.png" alt=""></p>
<p>GitHub 저장소에서 localletter-frontend 폴더에 화살표 아이콘이 붙어 있었고, 클릭해도 코드가 보이지 않았다.</p>
<pre><code class="language-bash">git ls-files -s localletter-frontend
160000 c085f971aae08fa5eeeb787ab869a473d5a30f77 0       localletter-frontend</code></pre>
<p>로컬에서 확인해보니, 해당 폴더가 서브모듈로 잘못 커밋된 상태였다.</p>
<ul>
<li>160000 → 서브모듈(gitlink) 모드</li>
<li>.gitmodules 파일이 없어 연결이 깨진 상태</li>
</ul>
<p><br/><br/></p>
<h3 id="2-원인">2. 원인</h3>
<ul>
<li><p>과거에 git submodule add 또는 비슷한 실수로 폴더를 서브모듈로 커밋함</p>
</li>
<li><p>.gitmodules 파일이 삭제되어 원격 연결 정보가 사라짐</p>
</li>
<li><p>깃허브에서 화살표 표시 후 다른 저장소로 연결하려 함 → 폴더 내용 확인 불가</p>
</li>
</ul>
<p><br/><br/></p>
<h3 id="3-해결-과정">3. 해결 과정</h3>
<h4 id="1-서브모듈-추적-제거">(1) 서브모듈 추적 제거</h4>
<pre><code class="language-bash"># 서브모듈 추적 제거 (파일은 그대로 유지)
git rm --cached localletter-frontend

# .gitmodules에서 해당 섹션 삭제 (있다면)
[ -f .gitmodules ] &amp;&amp; git add .gitmodules

# 로컬 서브모듈 메타데이터 삭제
rm -rf .git/modules/localletter-frontend</code></pre>
<h4 id="2-일반-폴더로-커밋">(2) 일반 폴더로 커밋</h4>
<pre><code>git add localletter-frontend
git commit -m &quot;Convert frontend from submodule to regular directory&quot;</code></pre><h4 id="3-push-시-거절-오류-발생">(3) push 시 거절 오류 발생</h4>
<pre><code class="language-bash">$ git push
To https://github.com/Choi-sh01/WebServer-Project.git
 ! [rejected]        main -&gt; main (fetch first)
error: failed to push some refs to &#39;https://github.com/Choi-sh01/WebServer-Project.git&#39;
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: &#39;git pull&#39; before pushing again.
hint: See the &#39;Note about fast-forwards&#39; in &#39;git push --help&#39; for details.
# ! [rejected] main -&gt; main (fetch first)</code></pre>
<p>→ 원격에 내가 없는 최신 커밋이 있어서 non-fast-forward 거절</p>
<h4 id="4-원격-변경-반영-후-푸시">(4) 원격 변경 반영 후 푸시</h4>
<pre><code class="language-bash"># 현재 브랜치가 main인지 확인
git branch

# 원격 최신 가져오기
git fetch origin

# 원격 main 위로 내 변경을 재배치(충돌 최소화)
git pull --rebase origin main

git push</code></pre>
<p><br/><br/></p>
<h3 id="4-배운-점">4. 배운 점</h3>
<ul>
<li>160000 모드는 서브모듈의 대표적인 표시</li>
<li>.gitmodules가 없는데 폴더가 서브모듈로 보이면, 연결이 깨진 상태</li>
<li>깃허브에서 서브모듈 → 일반 폴더 전환은 git rm --cached로 추적만 제거 후 다시 커밋</li>
<li>push 거절 시 무조건 force push가 아니라, 먼저 pull/rebase로 병합하는 습관이 안전</li>
</ul>
<p><br/><br/></p>
<h3 id="5-참고-명령어">5. 참고 명령어</h3>
<pre><code class="language-bash"># 서브모듈 상태 확인
git submodule status
git ls-files -s &lt;폴더명&gt;

# 서브모듈 제거 → 일반 폴더 전환
git rm --cached &lt;폴더명&gt;
rm -rf .git/modules/&lt;폴더명&gt;
git add &lt;폴더명&gt;
git commit -m &quot;Convert submodule to folder&quot;
git push

</code></pre>
]]></description>
        </item>
    </channel>
</rss>