<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>awake123 님의 블로그</title>
    <link>https://awake123.tistory.com/</link>
    <description>awake123 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Fri, 12 Jun 2026 06:18:12 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>awake123</managingEditor>
    <item>
      <title>데이터 베이스의 락</title>
      <link>https://awake123.tistory.com/66</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 베이스에서 락이란&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 트랜잭션이 동시에 같은 데이터를 읽고/수정하려고 할 때 **데이터의 정합성(일관성)**을 지키기 위해 &amp;ldquo;잠금&amp;rdquo;을 걸어 &lt;b&gt;접근을 제어&lt;/b&gt;하는 메커니즘&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 동시성(성능) &amp;harr; 일관성(정확성) 균형&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;166&quot; data-start=&quot;151&quot; data-ke-size=&quot;size26&quot;&gt;락이 필요한 대표 상황&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;368&quot; data-start=&quot;167&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;237&quot; data-start=&quot;167&quot;&gt;&lt;b&gt;Lost Update(갱신 분실)&lt;/b&gt;: A와 B가 같은 행을 읽고 각각 수정 &amp;rarr; 나중에 커밋한 값이 앞선 변경을 덮어씀&lt;/li&gt;
&lt;li data-end=&quot;282&quot; data-start=&quot;238&quot;&gt;&lt;b&gt;Dirty Read&lt;/b&gt;: 아직 커밋 안 된 데이터를 다른 트랜잭션이 읽음&lt;/li&gt;
&lt;li data-end=&quot;368&quot; data-start=&quot;283&quot;&gt;&lt;b&gt;Non-repeatable Read / Phantom Read&lt;/b&gt;: 같은 조회를 두 번 했는데 중간에 다른 트랜잭션이 수정/삽입해서 결과가 달라짐&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;409&quot; data-start=&quot;375&quot; data-ke-size=&quot;size26&quot;&gt;1) 락의 기본 종류: Shared / Exclusive&lt;/h2&gt;
&lt;h3 data-end=&quot;440&quot; data-start=&quot;410&quot; data-ke-size=&quot;size23&quot;&gt;Shared Lock (S Lock, 읽기 락)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;506&quot; data-start=&quot;441&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;452&quot; data-start=&quot;441&quot;&gt;&amp;ldquo;읽을게요&amp;rdquo; 잠금&lt;/li&gt;
&lt;li data-end=&quot;478&quot; data-start=&quot;453&quot;&gt;여러 트랜잭션이 &lt;b&gt;동시에 S락&lt;/b&gt;은 가능&lt;/li&gt;
&lt;li data-end=&quot;506&quot; data-start=&quot;479&quot;&gt;하지만 누군가가 X락(쓰기)을 걸려 하면 충돌&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;541&quot; data-start=&quot;508&quot; data-ke-size=&quot;size23&quot;&gt;Exclusive Lock (X Lock, 쓰기 락)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;613&quot; data-start=&quot;542&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;554&quot; data-start=&quot;542&quot;&gt;&amp;ldquo;수정할게요&amp;rdquo; 잠금&lt;/li&gt;
&lt;li data-end=&quot;584&quot; data-start=&quot;555&quot;&gt;&lt;b&gt;단독&lt;/b&gt;으로만 가능 (다른 S/X 모두 막음)&lt;/li&gt;
&lt;li data-end=&quot;613&quot; data-start=&quot;585&quot;&gt;UPDATE/DELETE 같은 쓰기 작업에 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;644&quot; data-start=&quot;620&quot; data-ke-size=&quot;size26&quot;&gt;2) 락의 범위(Granularity)&lt;/h2&gt;
&lt;p data-end=&quot;676&quot; data-start=&quot;645&quot; data-ke-size=&quot;size16&quot;&gt;DB는 상황에 따라 잠금을 &amp;ldquo;어디까지&amp;rdquo; 걸지 결정합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;891&quot; data-start=&quot;678&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;716&quot; data-start=&quot;678&quot;&gt;&lt;b&gt;Row Lock(행 락)&lt;/b&gt;: 특정 행만 잠금 &amp;rarr; 동시성 좋음&lt;/li&gt;
&lt;li data-end=&quot;756&quot; data-start=&quot;717&quot;&gt;&lt;b&gt;Page/Block Lock&lt;/b&gt;: 특정 페이지(블록) 단위 잠금&lt;/li&gt;
&lt;li data-end=&quot;820&quot; data-start=&quot;757&quot;&gt;&lt;b&gt;Table Lock(테이블 락)&lt;/b&gt;: 테이블 전체 잠금 &amp;rarr; 동시성 나쁨, 대신 관리 단순/빠른 경우도 있음&lt;/li&gt;
&lt;li data-end=&quot;891&quot; data-start=&quot;821&quot;&gt;&lt;b&gt;Index Lock / Key-range Lock(범위 락)&lt;/b&gt;: 인덱스 범위를 잠가 팬텀을 막는 방식(격리수준 관련)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;920&quot; data-start=&quot;898&quot; data-ke-size=&quot;size26&quot;&gt;3) 트랜잭션 격리수준과 락의 관계&lt;/h2&gt;
&lt;p data-end=&quot;964&quot; data-start=&quot;921&quot; data-ke-size=&quot;size16&quot;&gt;격리수준이 높을수록 &amp;ldquo;안전&amp;rdquo;하지만 보통 락/대기 증가로 성능 부담이 커집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1198&quot; data-start=&quot;966&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1024&quot; data-start=&quot;966&quot;&gt;&lt;b&gt;READ UNCOMMITTED&lt;/b&gt;: 커밋 안 된 데이터도 읽을 수 있음(Dirty Read 가능)&lt;/li&gt;
&lt;li data-end=&quot;1073&quot; data-start=&quot;1025&quot;&gt;&lt;b&gt;READ COMMITTED&lt;/b&gt;: 커밋된 것만 읽음(Dirty Read 방지)&lt;/li&gt;
&lt;li data-end=&quot;1152&quot; data-start=&quot;1074&quot;&gt;&lt;b&gt;REPEATABLE READ&lt;/b&gt;: 같은 행을 반복 조회해도 동일(Non-repeatable 방지), DB에 따라 팬텀은 남을 수 있음&lt;/li&gt;
&lt;li data-end=&quot;1198&quot; data-start=&quot;1153&quot;&gt;&lt;b&gt;SERIALIZABLE&lt;/b&gt;: 가장 강함(팬텀도 방지), 동시성 가장 떨어짐&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-end=&quot;1277&quot; data-start=&quot;1200&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1277&quot; data-start=&quot;1202&quot; data-ke-size=&quot;size16&quot;&gt;실무에선 &amp;ldquo;격리수준&amp;rdquo;이 곧 &amp;ldquo;락/버전관리 전략&amp;rdquo;과 붙어 있고, DB마다 구현이 달라요. (예: 어떤 DB는 MVCC로 읽기 락을 줄임)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1316&quot; data-start=&quot;1284&quot; data-ke-size=&quot;size26&quot;&gt;4) 비관적 락 vs 낙관적 락 (애플리케이션 관점)&lt;/h2&gt;
&lt;h3 data-end=&quot;1339&quot; data-start=&quot;1317&quot; data-ke-size=&quot;size23&quot;&gt;비관적 락(Pessimistic)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1428&quot; data-start=&quot;1340&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1360&quot; data-start=&quot;1340&quot;&gt;&amp;ldquo;충돌 날 것 같으니 미리 잠금&amp;rdquo;&lt;/li&gt;
&lt;li data-end=&quot;1377&quot; data-start=&quot;1361&quot;&gt;DB 락을 강하게 사용&lt;/li&gt;
&lt;li data-end=&quot;1428&quot; data-start=&quot;1378&quot;&gt;예: SELECT ... FOR UPDATE (읽으면서 수정 예정이니 X락 걸어라)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1495&quot; data-start=&quot;1430&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt;: 충돌 시 데이터 꼬임 방지에 강함&lt;br /&gt;&lt;b&gt;단점&lt;/b&gt;: 대기/교착상태(Deadlock) 가능, 동시성 감소&lt;/p&gt;
&lt;h3 data-end=&quot;1518&quot; data-start=&quot;1497&quot; data-ke-size=&quot;size23&quot;&gt;낙관적 락(Optimistic)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1637&quot; data-start=&quot;1519&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1540&quot; data-start=&quot;1519&quot;&gt;&amp;ldquo;충돌은 드물다, 커밋할 때 검사&amp;rdquo;&lt;/li&gt;
&lt;li data-end=&quot;1587&quot; data-start=&quot;1541&quot;&gt;보통 version 컬럼으로 체크 (JPA의 @Version 같은 방식)&lt;/li&gt;
&lt;li data-end=&quot;1637&quot; data-start=&quot;1588&quot;&gt;업데이트 시 WHERE id=? AND version=? 조건으로 성공/실패 판단&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1689&quot; data-start=&quot;1639&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt;: 락 대기 적고 동시성 좋음&lt;br /&gt;&lt;b&gt;단점&lt;/b&gt;: 충돌 발생 시 재시도 로직 필요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1715&quot; data-start=&quot;1696&quot; data-ke-size=&quot;size26&quot;&gt;5) 데드락(Deadlock)&lt;/h2&gt;
&lt;p data-end=&quot;1746&quot; data-start=&quot;1716&quot; data-ke-size=&quot;size16&quot;&gt;서로가 서로의 락을 기다리며 영원히 못 움직이는 상태.&lt;/p&gt;
&lt;p data-end=&quot;1750&quot; data-start=&quot;1748&quot; data-ke-size=&quot;size16&quot;&gt;예)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1809&quot; data-start=&quot;1751&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1777&quot; data-start=&quot;1751&quot;&gt;T1: A행 X락 &amp;rarr; B행 X락 시도(대기)&lt;/li&gt;
&lt;li data-end=&quot;1809&quot; data-start=&quot;1778&quot;&gt;T2: B행 X락 &amp;rarr; A행 X락 시도(대기)&lt;br /&gt;&amp;rarr; 교착&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1857&quot; data-start=&quot;1811&quot; data-ke-size=&quot;size16&quot;&gt;대부분 DB는 데드락을 감지해서 &lt;b&gt;한 트랜잭션을 롤백(희생자 선택)&lt;/b&gt; 시킵니다.&lt;/p&gt;
&lt;p data-end=&quot;1879&quot; data-start=&quot;1859&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예방 팁(실무에서 진짜 중요)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1993&quot; data-start=&quot;1880&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1912&quot; data-start=&quot;1880&quot;&gt;항상 &lt;b&gt;같은 순서&lt;/b&gt;로 락 획득(예: id 오름차순)&lt;/li&gt;
&lt;li data-end=&quot;1930&quot; data-start=&quot;1913&quot;&gt;트랜잭션을 &lt;b&gt;짧게&lt;/b&gt; 유지&lt;/li&gt;
&lt;li data-end=&quot;1962&quot; data-start=&quot;1931&quot;&gt;인덱스를 잘 잡아 불필요한 범위 락/테이블 스캔 방지&lt;/li&gt;
&lt;li data-end=&quot;1993&quot; data-start=&quot;1963&quot;&gt;&amp;ldquo;한 번에 많이 갱신&amp;rdquo; 배치 작업은 chunking&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;2027&quot; data-start=&quot;2000&quot; data-ke-size=&quot;size26&quot;&gt;6) (자주 헷갈리는 포인트) MVCC와 락&lt;/h2&gt;
&lt;p data-end=&quot;2153&quot; data-start=&quot;2028&quot; data-ke-size=&quot;size16&quot;&gt;많은 현대 DB는 **MVCC(다중 버전 동시성 제어)**로 &amp;ldquo;읽기&amp;rdquo;는 과거 버전을 보게 해서 &lt;b&gt;읽기 락을 줄입니다.&lt;/b&gt;&lt;br /&gt;하지만 &lt;b&gt;쓰기-쓰기 충돌&lt;/b&gt;이나 **특정 격리수준(팬텀 방지)**에서는 여전히 락이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;2183&quot; data-start=&quot;2160&quot; data-ke-size=&quot;size26&quot;&gt;스프링/JPA 실무 기준으로 감 잡기&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2413&quot; data-start=&quot;2184&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2301&quot; data-start=&quot;2184&quot;&gt;동시 수정이 자주 일어나는 &amp;ldquo;수량/좌석/재고/포인트&amp;rdquo; 같은 곳
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2301&quot; data-start=&quot;2223&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2265&quot; data-start=&quot;2223&quot;&gt;충돌이 잦으면: &lt;b&gt;비관적 락(SELECT FOR UPDATE)&lt;/b&gt; 고려&lt;/li&gt;
&lt;li data-end=&quot;2301&quot; data-start=&quot;2268&quot;&gt;충돌이 드물면: &lt;b&gt;낙관적 락(@Version)&lt;/b&gt; 선호&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2345&quot; data-start=&quot;2302&quot;&gt;&amp;ldquo;조회만 많고 수정이 적은&amp;rdquo; 화면
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2345&quot; data-start=&quot;2325&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2345&quot; data-start=&quot;2325&quot;&gt;MVCC 덕에 보통 큰 문제 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2413&quot; data-start=&quot;2346&quot;&gt;&amp;ldquo;조회 후 계산해서 수정&amp;rdquo; 패턴(읽고 &amp;rarr; 계산 &amp;rarr; 업데이트)
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2413&quot; data-start=&quot;2384&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2413&quot; data-start=&quot;2384&quot;&gt;Lost Update 위험이 큼 &amp;rarr; 락 전략 필수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 개인적인 의문.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 예전에 DB를 이용할때 DB에 다수의 인원이 다양한 요청을 보내 DB가 멈춰버리던 현상을 락에 걸렸다고 표현하는 걸 들은적이 있는데 이거과 다른점인지?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;201&quot; data-start=&quot;76&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;129&quot; data-start=&quot;76&quot;&gt;&lt;b&gt;락(Lock)&lt;/b&gt; = DB가 동시성 제어를 위해 의도적으로 거는 잠금(정상 기능)&lt;/li&gt;
&lt;li data-end=&quot;201&quot; data-start=&quot;130&quot;&gt;&lt;b&gt;DB가 멈춘 것처럼 보임&lt;/b&gt; = 대개 락 때문에 &amp;ldquo;기다림&amp;rdquo;이 길어져서 그렇게 보이는 현상 (또는 교착, 과도한 부하)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;252&quot; data-start=&quot;203&quot; data-ke-size=&quot;size16&quot;&gt;둘은 &lt;b&gt;같은 개념이 아니라, 원인(락)과 결과(멈춘 것처럼 보임)&lt;/b&gt; 관계&lt;/p&gt;
&lt;p data-end=&quot;252&quot; data-start=&quot;203&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;289&quot; data-start=&quot;259&quot; data-ke-size=&quot;size26&quot;&gt;&amp;ldquo;DB가 멈춘다&amp;rdquo;가 실제로는 뭐였을 가능성이 큰가&lt;/h2&gt;
&lt;h3 data-end=&quot;312&quot; data-start=&quot;290&quot; data-ke-size=&quot;size23&quot;&gt;1) 락 대기(Lock Wait)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;425&quot; data-start=&quot;313&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;352&quot; data-start=&quot;313&quot;&gt;어떤 트랜잭션이 행/테이블을 잡고 오래 안 놓음(커밋/롤백 안 함)&lt;/li&gt;
&lt;li data-end=&quot;383&quot; data-start=&quot;353&quot;&gt;다른 트랜잭션들이 그 락이 풀릴 때까지 &lt;b&gt;대기&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;425&quot; data-start=&quot;384&quot;&gt;앱에서는 API가 응답이 늦어지거나 타임아웃 &amp;rarr; &amp;ldquo;DB 멈춤&amp;rdquo;처럼 체감&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;432&quot; data-start=&quot;427&quot; data-ke-size=&quot;size16&quot;&gt;대표 원인&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;521&quot; data-start=&quot;433&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;482&quot; data-start=&quot;433&quot;&gt;트랜잭션을 너무 길게 잡음 (대량 업데이트, 외부 API 호출을 트랜잭션 안에서 함)&lt;/li&gt;
&lt;li data-end=&quot;521&quot; data-start=&quot;483&quot;&gt;인덱스가 없어 범위를 넓게 잠금(테이블 스캔) &amp;rarr; 락 범위가 커짐&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;548&quot; data-start=&quot;528&quot; data-ke-size=&quot;size23&quot;&gt;2) 데드락(Deadlock)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;648&quot; data-start=&quot;549&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;572&quot; data-start=&quot;549&quot;&gt;서로가 서로의 락을 기다리는 교착 상태&lt;/li&gt;
&lt;li data-end=&quot;619&quot; data-start=&quot;573&quot;&gt;DB가 &amp;ldquo;영원히 멈추게&amp;rdquo; 두진 않고, 보통 &lt;b&gt;한쪽 트랜잭션을 강제 롤백&lt;/b&gt;시킴&lt;/li&gt;
&lt;li data-end=&quot;648&quot; data-start=&quot;620&quot;&gt;앱에서는 간헐적으로 오류가 나거나 재시도가 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;684&quot; data-start=&quot;655&quot; data-ke-size=&quot;size23&quot;&gt;3) 리소스 병목(부하로 인한 정지처럼 보임)&lt;/h3&gt;
&lt;p data-end=&quot;707&quot; data-start=&quot;685&quot; data-ke-size=&quot;size16&quot;&gt;이건 &amp;ldquo;락&amp;rdquo;이 원인이 아닐 수도 있어요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;777&quot; data-start=&quot;708&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;751&quot; data-start=&quot;708&quot;&gt;CPU 100%, 디스크 I/O 포화, 커넥션 풀 고갈, 슬로우 쿼리 폭증&lt;/li&gt;
&lt;li data-end=&quot;777&quot; data-start=&quot;752&quot;&gt;결과적으로 요청이 쌓여서 &amp;ldquo;멈춤&amp;rdquo;처럼 보임&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;853&quot; data-start=&quot;779&quot; data-ke-size=&quot;size16&quot;&gt;다만 &lt;b&gt;락 대기&lt;/b&gt;도 결국은 &lt;b&gt;대기 요청이 쌓여서 DB/애플리케이션 리소스를 같이 잡아먹기 때문에&lt;/b&gt; 부하처럼 보일 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;853&quot; data-start=&quot;779&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;853&quot; data-start=&quot;779&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;853&quot; data-start=&quot;779&quot; data-ke-size=&quot;size20&quot;&gt;* 락을 거는 주체는?&lt;/h4&gt;
&lt;p data-end=&quot;853&quot; data-start=&quot;779&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;대부분의 락은 DB가 &amp;ldquo;자동으로&amp;rdquo; 걸고&lt;/b&gt;, 필요할 때만 사람이 &lt;b&gt;의도적으로 더 강하게&lt;/b&gt; 걸어 제어&lt;/p&gt;
&lt;h2 data-end=&quot;95&quot; data-start=&quot;72&quot; data-ke-size=&quot;size26&quot;&gt;1) DB가 자동으로 거는 락(기본)&lt;/h2&gt;
&lt;p data-end=&quot;102&quot; data-start=&quot;96&quot; data-ke-size=&quot;size16&quot;&gt;트랜잭션에서&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;207&quot; data-start=&quot;103&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;159&quot; data-start=&quot;103&quot;&gt;UPDATE / DELETE / INSERT 하면 DB가 알아서 &lt;b&gt;쓰기 락(X락)&lt;/b&gt; 걸고,&lt;/li&gt;
&lt;li data-end=&quot;207&quot; data-start=&quot;160&quot;&gt;격리수준/엔진 방식에 따라 읽기에도 필요한 제어(MVCC 포함)를 자동.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;266&quot; data-start=&quot;209&quot; data-ke-size=&quot;size16&quot;&gt;그래서 보통 개발자가 &amp;ldquo;락을 걸자&amp;rdquo;라고 코드에 쓰지 않아도, &lt;b&gt;쓰기 작업 자체가 락을 동반&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;266&quot; data-start=&quot;209&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;266&quot; data-start=&quot;209&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;301&quot; data-start=&quot;273&quot; data-ke-size=&quot;size26&quot;&gt;2) 사람이 의도적으로 거는 락(명시적/강제)&lt;/h2&gt;
&lt;p data-end=&quot;360&quot; data-start=&quot;302&quot; data-ke-size=&quot;size16&quot;&gt;충돌이 잦거나 &amp;ldquo;읽고 나서 반드시 수정&amp;rdquo; 같은 흐름을 안전하게 만들려면 개발자가 명시적으로 락을 겁니다.&lt;/p&gt;
&lt;p data-end=&quot;367&quot; data-start=&quot;362&quot; data-ke-size=&quot;size16&quot;&gt;대표 예시&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;603&quot; data-start=&quot;368&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;451&quot; data-start=&quot;368&quot;&gt;&lt;b&gt;비관적 락&lt;/b&gt;: SELECT ... FOR UPDATE&lt;br /&gt;&amp;rarr; &amp;ldquo;지금 읽는 이 행은 내가 곧 수정할 거니까 다른 수정 못 하게 잡아둬&amp;rdquo;&lt;/li&gt;
&lt;li data-end=&quot;488&quot; data-start=&quot;452&quot;&gt;(DB별로) &lt;b&gt;테이블 락&lt;/b&gt;: LOCK TABLE ...&lt;/li&gt;
&lt;li data-end=&quot;539&quot; data-start=&quot;489&quot;&gt;&lt;b&gt;JPA&lt;/b&gt;에선 @Lock(PESSIMISTIC_WRITE) 같은 걸로 걸기도 함&lt;/li&gt;
&lt;li data-end=&quot;603&quot; data-start=&quot;540&quot;&gt;&lt;b&gt;낙관적 락&lt;/b&gt;은 &amp;ldquo;락을 거는&amp;rdquo; 느낌이라기보다 version으로 &lt;b&gt;충돌 감지&lt;/b&gt;를 사람이 설계하는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;853&quot; data-start=&quot;779&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;638&quot; data-start=&quot;610&quot; data-ke-size=&quot;size26&quot;&gt;3) 그럼 &amp;ldquo;락 문제&amp;rdquo;를 누가 만들기도 하냐?&lt;/h2&gt;
&lt;p data-end=&quot;695&quot; data-start=&quot;639&quot; data-ke-size=&quot;size16&quot;&gt;락 자체는 정상 기능인데, 아래는 사람이 만든 코드/설계 때문에 락이 &lt;b&gt;길어져서&lt;/b&gt; 사고가 납니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;838&quot; data-start=&quot;697&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;744&quot; data-start=&quot;697&quot;&gt;트랜잭션을 길게 잡음 (트랜잭션 안에서 외부 API 호출, 사용자 입력 대기 등)&lt;/li&gt;
&lt;li data-end=&quot;779&quot; data-start=&quot;745&quot;&gt;인덱스가 없어 업데이트/조회 범위가 커짐 &amp;rarr; 락 범위 확대&lt;/li&gt;
&lt;li data-end=&quot;807&quot; data-start=&quot;780&quot;&gt;같은 자원을 서로 다른 순서로 잠금 &amp;rarr; 데드락&lt;/li&gt;
&lt;li data-end=&quot;838&quot; data-start=&quot;808&quot;&gt;한 번에 대량 갱신(배치)으로 많은 행을 오래 잠금&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;* DB별로 락을 거는 법과 락에 의해 발생하는 현상이 모두 다른지?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) DB별 락 거는 법&lt;/p&gt;
&lt;h3 data-end=&quot;225&quot; data-start=&quot;207&quot; data-ke-size=&quot;size23&quot;&gt;MySQL (InnoDB)&lt;/h3&gt;
&lt;p data-end=&quot;241&quot; data-start=&quot;226&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;행 잠금(비관적 락)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;358&quot; data-start=&quot;242&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;291&quot; data-start=&quot;242&quot;&gt;SELECT ... FOR UPDATE : 조회한 행을 &lt;b&gt;수정 목적&lt;/b&gt;으로 잠금&lt;/li&gt;
&lt;li data-end=&quot;358&quot; data-start=&quot;292&quot;&gt;SELECT ... FOR SHARE (또는 구버전 LOCK IN SHARE MODE) : 공유(읽기) 잠금&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;373&quot; data-start=&quot;360&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;경합 줄이기 옵션&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;479&quot; data-start=&quot;374&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;413&quot; data-start=&quot;374&quot;&gt;... FOR UPDATE NOWAIT : 못 잡으면 바로 실패&lt;/li&gt;
&lt;li data-end=&quot;479&quot; data-start=&quot;414&quot;&gt;... FOR UPDATE SKIP LOCKED : 잠긴 행은 건너뛰고 가져오기 (큐/작업자 패턴에 자주 씀)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;490&quot; data-start=&quot;481&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;테이블 락&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;552&quot; data-start=&quot;491&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;552&quot; data-start=&quot;491&quot;&gt;LOCK TABLES t WRITE/READ (주의: InnoDB라도 테이블 락을 강제로 걸 수 있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;576&quot; data-start=&quot;554&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;애플리케이션(Advisory) 락&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;648&quot; data-start=&quot;577&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;648&quot; data-start=&quot;577&quot;&gt;GET_LOCK('name', timeout), RELEASE_LOCK('name') : &amp;ldquo;업무용 뮤텍스&amp;rdquo; 같은 개념&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;669&quot; data-start=&quot;655&quot; data-ke-size=&quot;size23&quot;&gt;PostgreSQL&lt;/h3&gt;
&lt;p data-end=&quot;685&quot; data-start=&quot;670&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;행 잠금(비관적 락)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;804&quot; data-start=&quot;686&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;711&quot; data-start=&quot;686&quot;&gt;SELECT ... FOR UPDATE&lt;/li&gt;
&lt;li data-end=&quot;804&quot; data-start=&quot;712&quot;&gt;FOR NO KEY UPDATE / FOR SHARE / FOR KEY SHARE 처럼 더 세분화된 모드가 있음(외래키/키변경과 얽힐 때 의미가 커짐)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;812&quot; data-start=&quot;806&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;옵션&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;841&quot; data-start=&quot;813&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;841&quot; data-start=&quot;813&quot;&gt;NOWAIT, SKIP LOCKED 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;852&quot; data-start=&quot;843&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;테이블 락&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;892&quot; data-start=&quot;853&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;892&quot; data-start=&quot;853&quot;&gt;LOCK TABLE t IN ... MODE (여러 모드 존재)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;908&quot; data-start=&quot;894&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Advisory 락&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;981&quot; data-start=&quot;909&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;981&quot; data-start=&quot;909&quot;&gt;pg_advisory_lock(...), pg_advisory_unlock(...) (세션/트랜잭션 단위 둘 다 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;998&quot; data-start=&quot;988&quot; data-ke-size=&quot;size23&quot;&gt;Oracle&lt;/h3&gt;
&lt;p data-end=&quot;1007&quot; data-start=&quot;999&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;행 잠금&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1065&quot; data-start=&quot;1008&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1065&quot; data-start=&quot;1008&quot;&gt;SELECT ... FOR UPDATE [NOWAIT | WAIT n | SKIP LOCKED]&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1076&quot; data-start=&quot;1067&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;테이블 락&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1105&quot; data-start=&quot;1077&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1105&quot; data-start=&quot;1077&quot;&gt;LOCK TABLE t IN ... MODE&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1121&quot; data-start=&quot;1107&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;업무용 락(패키지)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1157&quot; data-start=&quot;1122&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1157&quot; data-start=&quot;1122&quot;&gt;DBMS_LOCK 등을 이용한 사용자 정의 락도 많이 씀&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1178&quot; data-start=&quot;1164&quot; data-ke-size=&quot;size23&quot;&gt;SQL Server&lt;/h3&gt;
&lt;p data-end=&quot;1230&quot; data-start=&quot;1179&quot; data-ke-size=&quot;size16&quot;&gt;SQL Server는 &amp;ldquo;구문&amp;rdquo;보다 &lt;b&gt;힌트/트랜잭션 격리수준&lt;/b&gt;으로 제어하는 경우가 많아요.&lt;/p&gt;
&lt;p data-end=&quot;1258&quot; data-start=&quot;1232&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;행/범위 잠금 힌트(SELECT에 붙임)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1415&quot; data-start=&quot;1259&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1298&quot; data-start=&quot;1259&quot;&gt;WITH (UPDLOCK) : 읽으면서 업데이트 락(수정 예정)&lt;/li&gt;
&lt;li data-end=&quot;1322&quot; data-start=&quot;1299&quot;&gt;WITH (XLOCK) : 배타 락&lt;/li&gt;
&lt;li data-end=&quot;1368&quot; data-start=&quot;1323&quot;&gt;WITH (HOLDLOCK) : 범위를 오래 유지(사실상 더 강한 일관성)&lt;/li&gt;
&lt;li data-end=&quot;1415&quot; data-start=&quot;1369&quot;&gt;ROWLOCK, PAGLOCK, TABLOCK 등으로 잠금 단위 힌트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1431&quot; data-start=&quot;1417&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Advisory 락&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1482&quot; data-start=&quot;1432&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1482&quot; data-start=&quot;1432&quot;&gt;sp_getapplock / sp_releaseapplock : 애플리케이션 락&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) DB별 락에 의한 현상&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1607&quot; data-start=&quot;1574&quot; data-ke-size=&quot;size23&quot;&gt;공통으로 나타나는 현상(대부분 DB에서 똑같이 체감)&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1631&quot; data-start=&quot;1608&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1631&quot; data-start=&quot;1608&quot;&gt;&lt;b&gt;Blocking(락 대기)&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1701&quot; data-start=&quot;1632&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1671&quot; data-start=&quot;1632&quot;&gt;어떤 트랜잭션이 락을 쥐고 있고 &amp;rarr; 다른 트랜잭션이 줄줄이 대기&lt;/li&gt;
&lt;li data-end=&quot;1701&quot; data-start=&quot;1672&quot;&gt;앱에서는 &amp;ldquo;DB 멈춘 듯&amp;rdquo; 보이는 가장 흔한 원인&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1726&quot; data-start=&quot;1703&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1726&quot; data-start=&quot;1703&quot;&gt;&lt;b&gt;Deadlock(교착상태)&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1784&quot; data-start=&quot;1727&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1751&quot; data-start=&quot;1727&quot;&gt;서로 필요한 락을 교차로 잡고 기다림&lt;/li&gt;
&lt;li data-end=&quot;1784&quot; data-start=&quot;1752&quot;&gt;보통 DB가 감지해서 한쪽을 강제 롤백(에러 발생)시킴&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1827&quot; data-start=&quot;1786&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1827&quot; data-start=&quot;1786&quot;&gt;&lt;b&gt;Lock timeout / statement timeout&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1868&quot; data-start=&quot;1828&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1868&quot; data-start=&quot;1828&quot;&gt;일정 시간 기다리다 실패 (DB/설정/쿼리 옵션에 따라 메시지 다름)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1901&quot; data-start=&quot;1875&quot; data-ke-size=&quot;size26&quot;&gt;DB마다 달라지는 &amp;ldquo;대표적인 차이 포인트&amp;rdquo;&lt;/h2&gt;
&lt;h3 data-end=&quot;1960&quot; data-start=&quot;1930&quot; data-ke-size=&quot;size23&quot;&gt;1) 읽기가 막히는가? (MVCC/스냅샷 차이)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2283&quot; data-start=&quot;1961&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2085&quot; data-start=&quot;1961&quot;&gt;&lt;b&gt;PostgreSQL / Oracle&lt;/b&gt;: 기본적으로 읽기는 MVCC 기반이라 대부분의 경우 &amp;ldquo;쓰기 때문에 읽기가 막히는 느낌&amp;rdquo;이 덜함&lt;br /&gt;(대신 오래된 트랜잭션이 남아 있으면 정리/성능 문제가 생길 수 있음)&lt;/li&gt;
&lt;li data-end=&quot;2186&quot; data-start=&quot;2086&quot;&gt;&lt;b&gt;MySQL(InnoDB)&lt;/b&gt;: MVCC가 있지만, &lt;b&gt;격리수준/쿼리 패턴&lt;/b&gt;에 따라 FOR UPDATE 같은 잠금 읽기가 흔하고, 인덱스가 없으면 락 범위가 커지기 쉬움&lt;/li&gt;
&lt;li data-end=&quot;2283&quot; data-start=&quot;2187&quot;&gt;&lt;b&gt;SQL Server&lt;/b&gt;: 기본 READ COMMITTED는 읽기/쓰기 경합이 더 보일 수 있고, 스냅샷 계열(예: RCSI/SNAPSHOT)을 켜면 양상이 확 달라짐&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2311&quot; data-start=&quot;2285&quot; data-ke-size=&quot;size23&quot;&gt;2) &amp;ldquo;범위 락/팬텀 방지&amp;rdquo; 방식이 다름&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2640&quot; data-start=&quot;2312&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2450&quot; data-start=&quot;2312&quot;&gt;&lt;b&gt;MySQL(InnoDB)&lt;/b&gt;: (특히 REPEATABLE READ에서) 인덱스를 기반으로 &lt;b&gt;next-key(레코드+갭) 락&lt;/b&gt; 같은 &amp;ldquo;범위 잠금&amp;rdquo;이 생겨서 생각보다 많이 막히는 경우가 있음&lt;br /&gt;&amp;rarr; 인덱스 유무/조건이 락 범위에 큰 영향&lt;/li&gt;
&lt;li data-end=&quot;2578&quot; data-start=&quot;2451&quot;&gt;&lt;b&gt;PostgreSQL&lt;/b&gt;: SERIALIZABLE에서 내부적으로 충돌을 탐지하는 방식(SSI)이라, &amp;ldquo;잠금으로 막는다&amp;rdquo; 느낌이 아니라 &lt;b&gt;커밋 시점에 실패&lt;/b&gt;(serialization failure)로 나타나는 경우가 있음&lt;/li&gt;
&lt;li data-end=&quot;2640&quot; data-start=&quot;2579&quot;&gt;&lt;b&gt;Oracle&lt;/b&gt;: 읽기 일관성이 강하고, 팬텀/일관성은 방식이 다르게 체감됨(읽기가 잘 안 막히는 편)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2665&quot; data-start=&quot;2642&quot; data-ke-size=&quot;size23&quot;&gt;3) 잠금 단위/에스컬레이션(승격)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2857&quot; data-start=&quot;2666&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2738&quot; data-start=&quot;2666&quot;&gt;&lt;b&gt;SQL Server&lt;/b&gt;는 &amp;ldquo;행&amp;rarr;페이지&amp;rarr;테이블&amp;rdquo;처럼 &lt;b&gt;락 에스컬레이션&lt;/b&gt;이 이슈로 자주 등장합니다(설정/상황에 따라).&lt;/li&gt;
&lt;li data-end=&quot;2857&quot; data-start=&quot;2739&quot;&gt;&lt;b&gt;MySQL/InnoDB, PostgreSQL&lt;/b&gt;도 잠금 단위 개념은 있지만, SQL Server만큼 &amp;ldquo;에스컬레이션 때문에 갑자기 테이블이 막힘&amp;rdquo;이 대표 밈처럼 자주 나오진 않는 편(대신 다른 요인이 큼).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2885&quot; data-start=&quot;2859&quot; data-ke-size=&quot;size23&quot;&gt;4) &amp;ldquo;문제 징후&amp;rdquo;가 보이는 방식이 다름&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3177&quot; data-start=&quot;2886&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2947&quot; data-start=&quot;2886&quot;&gt;MySQL: &amp;ldquo;Lock wait timeout&amp;rdquo;, &amp;ldquo;Deadlock found&amp;hellip;&amp;rdquo; 같은 메시지로 자주 인지&lt;/li&gt;
&lt;li data-end=&quot;3055&quot; data-start=&quot;2948&quot;&gt;PostgreSQL: &amp;ldquo;deadlock detected&amp;rdquo;도 있지만 SERIALIZABLE이면 &amp;ldquo;could not serialize access&amp;hellip;&amp;rdquo;처럼 &lt;b&gt;재시도&lt;/b&gt;를 전제로 한 실패가 나옴&lt;/li&gt;
&lt;li data-end=&quot;3126&quot; data-start=&quot;3056&quot;&gt;SQL Server: deadlock victim, lock request time out period exceeded 등&lt;/li&gt;
&lt;li data-end=&quot;3177&quot; data-start=&quot;3127&quot;&gt;Oracle: NOWAIT/WAIT 옵션에 따른 에러, 대기 이벤트로 보이는 경우 많음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;3221&quot; data-start=&quot;3184&quot; data-ke-size=&quot;size26&quot;&gt;&amp;ldquo;DB가 멈춘 것 같다&amp;rdquo;를 락으로 의심할 때 3가지&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;3342&quot; data-start=&quot;3222&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;3257&quot; data-start=&quot;3222&quot;&gt;**대기 체인(blocking chain)**이 있는지&lt;/li&gt;
&lt;li data-end=&quot;3300&quot; data-start=&quot;3258&quot;&gt;&lt;b&gt;오래 열린 트랜잭션&lt;/b&gt;이 있는지(커밋 안 하고 잡고 있는 세션)&lt;/li&gt;
&lt;li data-end=&quot;3342&quot; data-start=&quot;3301&quot;&gt;&lt;b&gt;인덱스 부재로 락 범위가 커진 것&lt;/b&gt;인지(조건 컬럼 인덱스 확인)&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Daily Dev Q&amp;amp;A</category>
      <author>awake123</author>
      <guid isPermaLink="true">https://awake123.tistory.com/66</guid>
      <comments>https://awake123.tistory.com/66#entry66comment</comments>
      <pubDate>Sun, 1 Feb 2026 22:23:13 +0900</pubDate>
    </item>
    <item>
      <title>http와 https의 차이</title>
      <link>https://awake123.tistory.com/63</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. http&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTTP(HyperText Transfer Protocol)&lt;/b&gt; 는 웹에서 &lt;b&gt;클라이언트(브라우저)&lt;/b&gt; 가 &lt;b&gt;서버&lt;/b&gt; 에 &amp;ldquo;리소스(HTML, JSON, 이미지 등)를 달라&amp;rdquo;라고 요청하고, 서버가 응답하는 &lt;b&gt;통신 규칙(프로토콜)&lt;/b&gt;.&lt;br /&gt;즉, 웹의 기본 대화 방식.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;234&quot; data-start=&quot;216&quot; data-ke-size=&quot;size26&quot;&gt;HTTP가 하는 일 (역할)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;350&quot; data-start=&quot;235&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;268&quot; data-start=&quot;235&quot;&gt;브라우저 &amp;rarr; 서버로 &lt;b&gt;요청(Request)&lt;/b&gt; 을 보냄&lt;/li&gt;
&lt;li data-end=&quot;303&quot; data-start=&quot;269&quot;&gt;서버 &amp;rarr; 브라우저로 &lt;b&gt;응답(Response)&lt;/b&gt; 을 보냄&lt;/li&gt;
&lt;li data-end=&quot;350&quot; data-start=&quot;304&quot;&gt;이 과정을 통해 페이지가 뜨거나(HTML), API 응답(JSON)을 받습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;366&quot; data-start=&quot;352&quot; data-ke-size=&quot;size26&quot;&gt;HTTP의 핵심 특징&lt;/h2&gt;
&lt;h3 data-end=&quot;382&quot; data-start=&quot;367&quot; data-ke-size=&quot;size23&quot;&gt;1) 요청/응답 구조&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;436&quot; data-start=&quot;383&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;411&quot; data-start=&quot;383&quot;&gt;&lt;b&gt;요청&lt;/b&gt;: 메서드 + 경로 + 헤더 + 바디&lt;/li&gt;
&lt;li data-end=&quot;436&quot; data-start=&quot;412&quot;&gt;&lt;b&gt;응답&lt;/b&gt;: 상태코드 + 헤더 + 바디&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;458&quot; data-start=&quot;438&quot; data-ke-size=&quot;size16&quot;&gt;예) API 호출의 전형적인 흐름&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;516&quot; data-start=&quot;459&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;488&quot; data-start=&quot;459&quot;&gt;GET /users/1 &amp;rarr; 유저 정보 달라&lt;/li&gt;
&lt;li data-end=&quot;516&quot; data-start=&quot;489&quot;&gt;서버 응답: 200 OK + JSON 바디&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;540&quot; data-start=&quot;518&quot; data-ke-size=&quot;size23&quot;&gt;2) Stateless (무상태)&lt;/h3&gt;
&lt;p data-end=&quot;602&quot; data-start=&quot;541&quot; data-ke-size=&quot;size16&quot;&gt;HTTP 자체는 &lt;b&gt;이전 요청을 기억하지 않음&lt;/b&gt;.&lt;br /&gt;그래서 로그인 같은 &amp;ldquo;상태&amp;rdquo;는 보통 아래로 보완합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;630&quot; data-start=&quot;603&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;614&quot; data-start=&quot;603&quot;&gt;&lt;b&gt;쿠키/세션&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;630&quot; data-start=&quot;615&quot;&gt;&lt;b&gt;토큰(JWT 등)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;655&quot; data-start=&quot;632&quot; data-ke-size=&quot;size23&quot;&gt;3) 사람이 읽기 쉬운 텍스트 기반&lt;/h3&gt;
&lt;p data-end=&quot;690&quot; data-start=&quot;656&quot; data-ke-size=&quot;size16&quot;&gt;요청 라인/헤더는 텍스트로 표현됩니다(그래서 디버깅이 쉬움).&lt;/p&gt;
&lt;h3 data-end=&quot;704&quot; data-start=&quot;692&quot; data-ke-size=&quot;size23&quot;&gt;4) 기본 포트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;770&quot; data-start=&quot;705&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;770&quot; data-start=&quot;705&quot;&gt;HTTP 기본 포트: &lt;b&gt;80&lt;/b&gt;&lt;br /&gt;(단, 실제 서비스는 리버스 프록시/로드밸런서 구성에 따라 달라질 수 있어요.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;791&quot; data-start=&quot;772&quot; data-ke-size=&quot;size26&quot;&gt;HTTP에서 자주 쓰는 메서드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;883&quot; data-start=&quot;792&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;805&quot; data-start=&quot;792&quot;&gt;&lt;b&gt;GET&lt;/b&gt;: 조회&lt;/li&gt;
&lt;li data-end=&quot;823&quot; data-start=&quot;806&quot;&gt;&lt;b&gt;POST&lt;/b&gt;: 생성/처리&lt;/li&gt;
&lt;li data-end=&quot;847&quot; data-start=&quot;824&quot;&gt;&lt;b&gt;PUT&lt;/b&gt;: 전체 수정(또는 대체)&lt;/li&gt;
&lt;li data-end=&quot;866&quot; data-start=&quot;848&quot;&gt;&lt;b&gt;PATCH&lt;/b&gt;: 부분 수정&lt;/li&gt;
&lt;li data-end=&quot;883&quot; data-start=&quot;867&quot;&gt;&lt;b&gt;DELETE&lt;/b&gt;: 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;904&quot; data-start=&quot;885&quot; data-ke-size=&quot;size26&quot;&gt;상태코드(응답 코드) 감 잡기&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1056&quot; data-start=&quot;905&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;942&quot; data-start=&quot;905&quot;&gt;&lt;b&gt;2xx&lt;/b&gt; 성공: 200 OK, 201 Created&lt;/li&gt;
&lt;li data-end=&quot;972&quot; data-start=&quot;943&quot;&gt;&lt;b&gt;3xx&lt;/b&gt; 리다이렉트: 301, 302&lt;/li&gt;
&lt;li data-end=&quot;1019&quot; data-start=&quot;973&quot;&gt;&lt;b&gt;4xx&lt;/b&gt; 클라이언트 오류: 400, 401, 403, 404&lt;/li&gt;
&lt;li data-end=&quot;1056&quot; data-start=&quot;1020&quot;&gt;&lt;b&gt;5xx&lt;/b&gt; 서버 오류: 500, 502, 503&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. https&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTTPS(HyperText Transfer Protocol Secure)&lt;/b&gt; 는 &lt;b&gt;HTTP에 보안 계층(TLS/SSL)&lt;/b&gt; 을 붙인 통신 방식.&lt;br /&gt;즉, &amp;ldquo;HTTP로 주고받되, 그 내용을 안전하게(암호화/검증/인증) 주고받는 프로토콜&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;216&quot; data-start=&quot;187&quot; data-ke-size=&quot;size16&quot;&gt;HTTPS는 아래 3가지를 보장하려고 만들어졌습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;397&quot; data-start=&quot;218&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;282&quot; data-start=&quot;218&quot;&gt;&lt;b&gt;기밀성(Confidentiality)&lt;/b&gt;: 전송 내용이 &lt;b&gt;암호화&lt;/b&gt;되어 중간에서 훔쳐봐도 읽기 어려움&lt;/li&gt;
&lt;li data-end=&quot;332&quot; data-start=&quot;283&quot;&gt;&lt;b&gt;무결성(Integrity)&lt;/b&gt;: 데이터가 &lt;b&gt;중간에서 변조&lt;/b&gt;되면 감지/차단&lt;/li&gt;
&lt;li data-end=&quot;397&quot; data-start=&quot;333&quot;&gt;&lt;b&gt;인증(Authentication)&lt;/b&gt;: 접속한 서버가 &lt;b&gt;진짜 그 서버인지&lt;/b&gt;(가짜 서버 방지) 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;430&quot; data-start=&quot;404&quot; data-ke-size=&quot;size26&quot;&gt;HTTPS는 어떻게 동작하나? (큰 흐름)&lt;/h2&gt;
&lt;p data-end=&quot;492&quot; data-start=&quot;431&quot; data-ke-size=&quot;size16&quot;&gt;HTTPS는 &amp;ldquo;HTTP 요청/응답&amp;rdquo;을 하기 전에, 먼저 &lt;b&gt;TLS 핸드셰이크&lt;/b&gt;로 안전한 통신 준비를 합니다.&lt;/p&gt;
&lt;h3 data-end=&quot;521&quot; data-start=&quot;494&quot; data-ke-size=&quot;size23&quot;&gt;1) TLS 핸드셰이크(보안 통로 만들기)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;719&quot; data-start=&quot;522&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;554&quot; data-start=&quot;522&quot;&gt;브라우저(클라이언트)가 서버에게 &amp;ldquo;보안 연결하자&amp;rdquo; 요청&lt;/li&gt;
&lt;li data-end=&quot;581&quot; data-start=&quot;555&quot;&gt;서버가 &lt;b&gt;인증서(서버의 신분증)&lt;/b&gt; 를 줌&lt;/li&gt;
&lt;li data-end=&quot;645&quot; data-start=&quot;582&quot;&gt;브라우저는 인증서가 &lt;b&gt;신뢰할 수 있는 CA(인증기관)&lt;/b&gt; 로부터 발급된 건지, 도메인/유효기간이 맞는지 확인&lt;/li&gt;
&lt;li data-end=&quot;686&quot; data-start=&quot;646&quot;&gt;확인이 끝나면, 양쪽이 &lt;b&gt;같은 대칭키(세션키)&lt;/b&gt; 를 안전하게 합의&lt;/li&gt;
&lt;li data-end=&quot;719&quot; data-start=&quot;687&quot;&gt;이후부터는 그 세션키로 데이터가 빠르게 암호화되어 오감&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;725&quot; data-start=&quot;721&quot; data-ke-size=&quot;size16&quot;&gt;핵심은:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;788&quot; data-start=&quot;726&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;749&quot; data-start=&quot;726&quot;&gt;&lt;b&gt;인증서로 &amp;lsquo;진짜 서버&amp;rsquo;인지 확인&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;788&quot; data-start=&quot;750&quot;&gt;이후 &lt;b&gt;세션키로 실제 데이터 암호화&lt;/b&gt; (대칭키가 빠르기 때문)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;817&quot; data-start=&quot;790&quot; data-ke-size=&quot;size23&quot;&gt;2) 그 다음에 &amp;ldquo;그냥 HTTP처럼&amp;rdquo; 통신&lt;/h3&gt;
&lt;p data-end=&quot;827&quot; data-start=&quot;818&quot; data-ke-size=&quot;size16&quot;&gt;보안 통로 위에서&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;902&quot; data-start=&quot;828&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;873&quot; data-start=&quot;828&quot;&gt;GET /&amp;hellip;, POST /&amp;hellip; 같은 HTTP 요청/응답이 그대로 오가는데&lt;/li&gt;
&lt;li data-end=&quot;902&quot; data-start=&quot;874&quot;&gt;&lt;b&gt;내용은 전부 암호화된 상태&lt;/b&gt;로 전송됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;921&quot; data-start=&quot;909&quot; data-ke-size=&quot;size26&quot;&gt;HTTPS의 특징&lt;/h2&gt;
&lt;h3 data-end=&quot;934&quot; data-start=&quot;922&quot; data-ke-size=&quot;size23&quot;&gt;1) 기본 포트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;957&quot; data-start=&quot;935&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;957&quot; data-start=&quot;935&quot;&gt;HTTPS 기본 포트: &lt;b&gt;443&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;972&quot; data-start=&quot;959&quot; data-ke-size=&quot;size23&quot;&gt;2) URL 표기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1021&quot; data-start=&quot;973&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;990&quot; data-start=&quot;973&quot;&gt;https:// 로 시작&lt;/li&gt;
&lt;li data-end=&quot;1021&quot; data-start=&quot;991&quot;&gt;브라우저 자물쇠 아이콘(인증서 유효 + 안전 연결)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1034&quot; data-start=&quot;1023&quot; data-ke-size=&quot;size23&quot;&gt;3) 성능은?&lt;/h3&gt;
&lt;p data-end=&quot;1066&quot; data-start=&quot;1035&quot; data-ke-size=&quot;size16&quot;&gt;예전엔 &amp;ldquo;HTTPS는 느리다&amp;rdquo; 인식이 있었는데, 지금은:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1169&quot; data-start=&quot;1067&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1169&quot; data-start=&quot;1067&quot;&gt;TLS 최적화, 세션 재사용, HTTP/2/HTTP/3 등으로&lt;br /&gt;&lt;b&gt;실사용에서 큰 차이 없거나 오히려 더 나은 경우도 많습니다.&lt;/b&gt;&lt;br /&gt;(단, 최초 연결 시 핸드셰이크 비용은 존재)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1207&quot; data-start=&quot;1176&quot; data-ke-size=&quot;size26&quot;&gt;단 HTTPS가 있어도 &amp;ldquo;완벽&amp;rdquo;은 아니다&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1419&quot; data-start=&quot;1208&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1279&quot; data-start=&quot;1208&quot;&gt;HTTPS는 &lt;b&gt;전송 구간&lt;/b&gt;을 보호합니다.&lt;br /&gt;서버 자체가 털리거나, 사용자 PC가 악성코드에 감염되면 별개 문제.&lt;/li&gt;
&lt;li data-end=&quot;1419&quot; data-start=&quot;1280&quot;&gt;HTTPS라고 해서 &amp;ldquo;그 사이트가 무조건 안전한 서비스/정상 기업&amp;rdquo;을 의미하진 않습니다.&lt;br /&gt;&lt;b&gt;도메인 소유&lt;/b&gt;를 증명하는 인증서도 많아서(기본 DV) 피싱에 악용될 수도 있어요.&lt;br /&gt;다만 &lt;b&gt;중간자 공격/도청/변조&lt;/b&gt;는 크게 막아줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;http를 사용하면 발생할수있는 취약점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;180&quot; data-start=&quot;150&quot; data-ke-size=&quot;size26&quot;&gt;1) 도청 스니핑 (Packet Sniffing)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;335&quot; data-start=&quot;181&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;233&quot; data-start=&quot;181&quot;&gt;같은 네트워크(공용 와이파이, 사내망 등)에서 공격자가 트래픽을 &lt;b&gt;가로채서 읽는&lt;/b&gt; 방식&lt;/li&gt;
&lt;li data-end=&quot;300&quot; data-start=&quot;234&quot;&gt;HTTP는 평문이라 &lt;b&gt;로그인 ID/PW, 쿠키, 폼 입력값, API 응답(JSON)&lt;/b&gt; 등이 그대로 보일 수 있음&lt;/li&gt;
&lt;li data-end=&quot;335&quot; data-start=&quot;301&quot;&gt;예: 공용 와이파이에서 HTTP 로그인 &amp;rarr; 계정 정보 노출&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;356&quot; data-start=&quot;337&quot; data-ke-size=&quot;size26&quot;&gt;2) 중간자 공격 (MITM)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;466&quot; data-start=&quot;357&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;400&quot; data-start=&quot;357&quot;&gt;공격자가 사용자&amp;harr;서버 사이에 끼어들어 &lt;b&gt;보거나/바꾸거나/가짜로 응답&lt;/b&gt;함&lt;/li&gt;
&lt;li data-end=&quot;435&quot; data-start=&quot;401&quot;&gt;HTTP는 &amp;ldquo;서버가 진짜인지&amp;rdquo; 확인이 약해서 더 쉽습니다.&lt;/li&gt;
&lt;li data-end=&quot;466&quot; data-start=&quot;436&quot;&gt;예: 로그인 페이지를 가짜로 바꿔서 비밀번호를 빼감&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;515&quot; data-start=&quot;468&quot; data-ke-size=&quot;size26&quot;&gt;3) 트래픽 변조 (Content Injection / Ad Injection)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;627&quot; data-start=&quot;516&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;562&quot; data-start=&quot;516&quot;&gt;통신 내용을 중간에서 &lt;b&gt;수정&lt;/b&gt;해서 악성 스크립트/광고/리다이렉트를 끼워 넣음&lt;/li&gt;
&lt;li data-end=&quot;627&quot; data-start=&quot;563&quot;&gt;예: HTTP로 받는 웹페이지에 &amp;lt;script&amp;gt;를 주입 &amp;rarr; 악성 사이트로 유도, 추가 악성코드 다운로드 유도&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;662&quot; data-start=&quot;629&quot; data-ke-size=&quot;size26&quot;&gt;4) 세션 하이재킹 (Session Hijacking)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;804&quot; data-start=&quot;663&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;712&quot; data-start=&quot;663&quot;&gt;로그인 후에 서버가 &amp;ldquo;너 로그인 됐어&amp;rdquo;라고 식별하는 값이 보통 &lt;b&gt;세션 쿠키&lt;/b&gt;인데,&lt;/li&gt;
&lt;li data-end=&quot;763&quot; data-start=&quot;713&quot;&gt;HTTP에서는 이 쿠키가 노출될 수 있어서 &lt;b&gt;비번 없이도&lt;/b&gt; 로그인 상태를 탈취 가능&lt;/li&gt;
&lt;li data-end=&quot;804&quot; data-start=&quot;764&quot;&gt;예: 쿠키를 훔쳐서 그대로 재사용 &amp;rarr; 내 계정으로 접속된 것처럼 행동&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;836&quot; data-start=&quot;806&quot; data-ke-size=&quot;size26&quot;&gt;5) 세션 고정 (Session Fixation)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;959&quot; data-start=&quot;837&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;877&quot; data-start=&quot;837&quot;&gt;공격자가 특정 세션 ID를 피해자에게 쓰게 만든 뒤(링크/주입 등),&lt;/li&gt;
&lt;li data-end=&quot;923&quot; data-start=&quot;878&quot;&gt;피해자가 로그인하면 그 세션이 &amp;ldquo;로그인된 세션&amp;rdquo;이 되어서 공격자가 그대로 탈취&lt;/li&gt;
&lt;li data-end=&quot;959&quot; data-start=&quot;924&quot;&gt;HTTP 환경에서 세션 관리가 허술하면 더 위험해질 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;HTTP 환경에서 더 쉽게 악용되지만, 근본 원인은 세션 재발급/검증 로직 미흡&amp;rdquo;&lt;/p&gt;
&lt;h2 data-end=&quot;1008&quot; data-start=&quot;961&quot; data-ke-size=&quot;size26&quot;&gt;6) 다운그레이드/SSL Stripping (HTTPS를 HTTP로 떨어뜨리기)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1154&quot; data-start=&quot;1009&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1062&quot; data-start=&quot;1009&quot;&gt;사용자가 원래 HTTPS로 갈 상황인데, 공격자가 중간에서 &amp;ldquo;HTTP로 접속하게&amp;rdquo; 유도/강제&lt;/li&gt;
&lt;li data-end=&quot;1131&quot; data-start=&quot;1063&quot;&gt;예: 첫 접속이 HTTP인 사이트에서 301/302를 가로채 HTTPS 이동을 막고 계속 HTTP로 사용하게 만들기&lt;/li&gt;
&lt;li data-end=&quot;1154&quot; data-start=&quot;1132&quot;&gt;(HSTS 적용이 약하면 특히 위험)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1175&quot; data-start=&quot;1156&quot; data-ke-size=&quot;size26&quot;&gt;7) DNS 스푸핑/피싱 결합&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1330&quot; data-start=&quot;1176&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1228&quot; data-start=&quot;1176&quot;&gt;DNS 응답을 속이거나 공유기/와이파이에서 DNS를 조작해 &lt;b&gt;가짜 서버로 보내는&lt;/b&gt; 방식&lt;/li&gt;
&lt;li data-end=&quot;1271&quot; data-start=&quot;1229&quot;&gt;HTTP면 그 가짜 서버가 진짜처럼 보이기 쉬움(인증서 검증이 없으니까)&lt;/li&gt;
&lt;li data-end=&quot;1330&quot; data-start=&quot;1272&quot;&gt;예: bank.example.com을 가짜 IP로 연결 &amp;rarr; 똑같은 로그인 화면 제공 &amp;rarr; 정보 탈취&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;DNS 조작 자체는 HTTPS에도 발생할 수 있으나, HTTPS는 인증서 검증으로 피해를 크게 줄인다.&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;https를 사용하지 않고 해당 취약점에 의한 피해를 줄이거나 일부 막을수 있는 우회방법&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;191&quot; data-start=&quot;154&quot; data-ke-size=&quot;size26&quot;&gt;1) 애플리케이션 레벨 암호화 (Payload 자체를 암호화)&lt;/h2&gt;
&lt;p data-end=&quot;231&quot; data-start=&quot;192&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전송 내용(바디)&lt;/b&gt; 을 앱에서 직접 암호화해서 보내는 방식입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;531&quot; data-start=&quot;232&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;285&quot; data-start=&quot;232&quot;&gt;예: 클라이언트가 AES-GCM 같은 대칭키로 JSON 바디를 암호화 &amp;rarr; 서버에서 복호화&lt;/li&gt;
&lt;li data-end=&quot;318&quot; data-start=&quot;286&quot;&gt;장점: 네트워크에서 바디를 훔쳐봐도 내용은 읽기 어려움&lt;/li&gt;
&lt;li data-end=&quot;531&quot; data-start=&quot;319&quot;&gt;한계(치명적):
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;531&quot; data-start=&quot;332&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;382&quot; data-start=&quot;332&quot;&gt;&lt;b&gt;헤더/URL/도메인&lt;/b&gt;은 여전히 노출(무슨 API를 호출하는지, 어떤 서버인지 등)&lt;/li&gt;
&lt;li data-end=&quot;441&quot; data-start=&quot;385&quot;&gt;&lt;b&gt;서버 인증&lt;/b&gt;이 없으면 키 교환 과정 자체가 MITM에 뚫림(키를 중간자가 갈아끼울 수 있음)&lt;/li&gt;
&lt;li data-end=&quot;531&quot; data-start=&quot;444&quot;&gt;구현 실수(키 관리/nonce 재사용/검증 누락)로 오히려 취약해질 가능성이 큼&lt;br /&gt;&amp;rarr; 보통 &amp;ldquo;HTTPS 위에 추가로&amp;rdquo; 쓰는 방식이지, 대체재로는 비추천&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;564&quot; data-start=&quot;533&quot; data-ke-size=&quot;size26&quot;&gt;2) HMAC/서명으로 변조 감지 (무결성만 확보)&lt;/h2&gt;
&lt;p data-end=&quot;619&quot; data-start=&quot;565&quot; data-ke-size=&quot;size16&quot;&gt;요청/응답에 &lt;b&gt;서명(HMAC 또는 공개키 서명)&lt;/b&gt; 을 붙여서 중간 변조를 감지하는 방식입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;832&quot; data-start=&quot;620&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;674&quot; data-start=&quot;620&quot;&gt;예: HMAC(secret, timestamp + body) 를 헤더로 보내고 서버가 검증&lt;/li&gt;
&lt;li data-end=&quot;698&quot; data-start=&quot;675&quot;&gt;장점: &lt;b&gt;변조&lt;/b&gt;는 막거나 탐지 가능&lt;/li&gt;
&lt;li data-end=&quot;832&quot; data-start=&quot;699&quot;&gt;한계:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;832&quot; data-start=&quot;707&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;734&quot; data-start=&quot;707&quot;&gt;내용은 여전히 &lt;b&gt;도청&lt;/b&gt; 가능(기밀성 없음)&lt;/li&gt;
&lt;li data-end=&quot;755&quot; data-start=&quot;737&quot;&gt;비밀키 공유/배포 문제가 남음&lt;/li&gt;
&lt;li data-end=&quot;832&quot; data-start=&quot;758&quot;&gt;재전송 공격(replay) 방지용 timestamp/nonce가 필요&lt;br /&gt;&amp;rarr; &amp;ldquo;변조 방지&amp;rdquo;에는 도움되지만, &amp;lsquo;보는 것&amp;rsquo;은 못 막음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;868&quot; data-start=&quot;834&quot; data-ke-size=&quot;size26&quot;&gt;3) 세션 탈취 완화 (쿠키/세션 운용으로 피해 줄이기)&lt;/h2&gt;
&lt;p data-end=&quot;919&quot; data-start=&quot;869&quot; data-ke-size=&quot;size16&quot;&gt;HTTPS 없이도 &lt;b&gt;세션 하이재킹 위험을 낮추는&lt;/b&gt; 운영은 가능해요(완전 차단은 아님).&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1148&quot; data-start=&quot;920&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;953&quot; data-start=&quot;920&quot;&gt;쿠키에 HttpOnly (JS로 쿠키 읽기 차단)&lt;/li&gt;
&lt;li data-end=&quot;996&quot; data-start=&quot;954&quot;&gt;세션을 &lt;b&gt;IP/UA/디바이스 지문&lt;/b&gt;에 바인딩(환경 바뀌면 세션 폐기)&lt;/li&gt;
&lt;li data-end=&quot;1030&quot; data-start=&quot;997&quot;&gt;세션 수명 짧게 + 민감 기능은 재인증(비밀번호/2FA)&lt;/li&gt;
&lt;li data-end=&quot;1057&quot; data-start=&quot;1031&quot;&gt;요청마다 CSRF 토큰/Origin 검사 등&lt;/li&gt;
&lt;li data-end=&quot;1148&quot; data-start=&quot;1058&quot;&gt;한계:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1148&quot; data-start=&quot;1066&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1092&quot; data-start=&quot;1066&quot;&gt;네트워크에서 쿠키가 보이면 그대로 탈취 가능&lt;/li&gt;
&lt;li data-end=&quot;1148&quot; data-start=&quot;1095&quot;&gt;공용망/NAT 환경에서 IP 바인딩은 오탐/불편 증가&lt;br /&gt;&amp;rarr; &amp;ldquo;탈취 후 악용&amp;rdquo;을 줄이는 수준&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1182&quot; data-start=&quot;1150&quot; data-ke-size=&quot;size26&quot;&gt;4) 네트워크를 &amp;lsquo;사설화&amp;rsquo;해서 통로 자체를 믿는 방식&lt;/h2&gt;
&lt;p data-end=&quot;1223&quot; data-start=&quot;1183&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;HTTP를 쓰되, 그 통신이 흐르는 망을 암호화/격리&amp;rdquo;하는 접근입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1460&quot; data-start=&quot;1224&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1256&quot; data-start=&quot;1224&quot;&gt;&lt;b&gt;VPN(사이트-투-사이트 / 클라이언트 VPN)&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1281&quot; data-start=&quot;1257&quot;&gt;&lt;b&gt;전용망, 사내망, 프라이빗 APN&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1302&quot; data-start=&quot;1282&quot;&gt;&lt;b&gt;SSH 터널링(포트포워딩)&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1340&quot; data-start=&quot;1303&quot;&gt;장점: 인터넷 구간 노출을 줄임(외부 스니핑/변조 난이도 상승)&lt;/li&gt;
&lt;li data-end=&quot;1460&quot; data-start=&quot;1341&quot;&gt;한계:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1460&quot; data-start=&quot;1349&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1395&quot; data-start=&quot;1349&quot;&gt;결국 &amp;ldquo;TLS를 다른 형태로 쓰는 것&amp;rdquo;이지, HTTP 자체가 안전해지는 건 아님&lt;/li&gt;
&lt;li data-end=&quot;1423&quot; data-start=&quot;1398&quot;&gt;VPN/터널 종단이 뚫리면 동일 문제 재발&lt;/li&gt;
&lt;li data-end=&quot;1460&quot; data-start=&quot;1426&quot;&gt;공용 사용자 대상 서비스엔 적용이 어렵거나 운영 부담이 큼&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1491&quot; data-start=&quot;1462&quot; data-ke-size=&quot;size26&quot;&gt;5) &amp;ldquo;사용자 실수/다운그레이드&amp;rdquo;만 줄이는 장치&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1609&quot; data-start=&quot;1492&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1523&quot; data-start=&quot;1492&quot;&gt;HTTPS를 쓰지 않는다고 했으니 이건 제한적이지만,&lt;/li&gt;
&lt;li data-end=&quot;1609&quot; data-start=&quot;1524&quot;&gt;최소한 &amp;ldquo;HTTP로 넘어가게 만드는 공격&amp;rdquo;을 줄이려면 HSTS 같은 게 필요함&lt;br /&gt;(다만 HSTS는 HTTPS가 전제라 여기선 실질적 해결책이 아님)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기밀성(도청 방지) + 무결성(변조 방지) + 서버 인증&lt;/b&gt;을 동시에 해결하려면 사실상 &lt;b&gt;TLS(=HTTPS)&lt;/b&gt; 가 정답&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1724&quot; data-start=&quot;1706&quot; data-ke-size=&quot;size16&quot;&gt;HTTPS 없이 할 수 있는 건:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1875&quot; data-start=&quot;1727&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1759&quot; data-start=&quot;1727&quot;&gt;&lt;b&gt;(a) 앱 데이터만 암호화&lt;/b&gt;해서 &amp;ldquo;내용만&amp;rdquo; 가리기&lt;/li&gt;
&lt;li data-end=&quot;1783&quot; data-start=&quot;1762&quot;&gt;&lt;b&gt;(b) 서명으로 변조만&lt;/b&gt; 막기&lt;/li&gt;
&lt;li data-end=&quot;1811&quot; data-start=&quot;1786&quot;&gt;&lt;b&gt;(c) 세션 정책으로 피해를 줄이기&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1838&quot; data-start=&quot;1814&quot;&gt;&lt;b&gt;(d) VPN/터널로 망을 감싸기&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;135&quot; data-start=&quot;109&quot; data-ke-size=&quot;size26&quot;&gt;HTTP의 실질적인 장점(HTTPS 대비)&lt;/h2&gt;
&lt;h3 data-end=&quot;151&quot; data-start=&quot;137&quot; data-ke-size=&quot;size23&quot;&gt;1) 설정이 단순함&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;223&quot; data-start=&quot;152&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;191&quot; data-start=&quot;152&quot;&gt;인증서 발급/갱신, 체인 문제, SNI, TLS 설정 같은 게 없음&lt;/li&gt;
&lt;li data-end=&quot;223&quot; data-start=&quot;192&quot;&gt;&amp;ldquo;로컬에서 잠깐 띄워서 확인&amp;rdquo; 같은 용도에 편합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;255&quot; data-start=&quot;225&quot; data-ke-size=&quot;size23&quot;&gt;2) 최초 연결 오버헤드가 더 적음(아주 미세)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;395&quot; data-start=&quot;256&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;285&quot; data-start=&quot;256&quot;&gt;TLS 핸드셰이크가 없어서 이론상 더 가볍습니다.&lt;/li&gt;
&lt;li data-end=&quot;395&quot; data-start=&quot;286&quot;&gt;다만 요즘은 TLS 1.3, 세션 재개, HTTP/2/3 최적화로 &lt;b&gt;체감 차이는 거의 없거나 오히려 HTTPS가 더 빠른 경우도&lt;/b&gt; 많습니다(HTTP/2/3는 보통 TLS와 같이 쓰이니까).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;427&quot; data-start=&quot;397&quot; data-ke-size=&quot;size23&quot;&gt;3) 디버깅/패킷 분석이 쉬움(보안 장비 없이)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;581&quot; data-start=&quot;428&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;465&quot; data-start=&quot;428&quot;&gt;네트워크에서 그냥 캡처하면 내용이 보이니 트러블슈팅이 쉽습니다.&lt;/li&gt;
&lt;li data-end=&quot;512&quot; data-start=&quot;466&quot;&gt;물론 이건 **&amp;ldquo;장점&amp;rdquo;이라기보다 &amp;ldquo;보안이 없어서 생기는 편의&amp;rdquo;**에 가깝습니다.&lt;/li&gt;
&lt;li data-end=&quot;581&quot; data-start=&quot;513&quot;&gt;HTTPS도 개발 환경에서는 프록시(Charles/Fiddler), 로컬 CA, 로그 등으로 충분히 디버깅 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;609&quot; data-start=&quot;583&quot; data-ke-size=&quot;size23&quot;&gt;4) 아주 제한된 환경에서의 적용 용이성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;715&quot; data-start=&quot;610&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;665&quot; data-start=&quot;610&quot;&gt;극도로 단순한 장치/레거시 시스템에서 TLS 스택이 부담될 수 있음(메모리/CPU/라이브러리).&lt;/li&gt;
&lt;li data-end=&quot;715&quot; data-start=&quot;666&quot;&gt;다만 요즘 IoT도 경량 TLS가 많고, 외부망이면 TLS가 사실상 필수에 가까워요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;756&quot; data-start=&quot;717&quot; data-ke-size=&quot;size23&quot;&gt;5) &amp;ldquo;민감 정보가 전혀 없는 내부 폐쇄망&amp;rdquo;에선 비용 대비 효율&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;858&quot; data-start=&quot;757&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;782&quot; data-start=&quot;757&quot;&gt;인터넷과 완전히 분리된 망(진짜 폐쇄)에서&lt;/li&gt;
&lt;li data-end=&quot;858&quot; data-start=&quot;783&quot;&gt;테스트용, 상태 페이지(health check) 등 &lt;b&gt;민감정보 0&lt;/b&gt;인 경우엔 HTTP가 운영 단순성 면에서 선택될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1063&quot; data-start=&quot;1033&quot;&gt;&lt;b&gt;HTTP의 장점 = 단순함/편의/레거시 호환&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1105&quot; data-start=&quot;1064&quot;&gt;&lt;b&gt;HTTPS의 가치 = 인터넷에서 서비스를 &amp;lsquo;성립&amp;rsquo;시키는 안전장치&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;개발 서버를 https로 사용하는법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;293&quot; data-start=&quot;253&quot; data-ke-size=&quot;size23&quot;&gt;A. mkcert로 &amp;ldquo;신뢰되는 로컬 인증서&amp;rdquo; 만들기 (가장 추천)&lt;/h3&gt;
&lt;p data-end=&quot;394&quot; data-start=&quot;294&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt;: 크롬/엣지에서 경고 없이 정상 HTTPS, 설정도 단순&lt;br /&gt;&lt;b&gt;개요&lt;/b&gt;: 내 PC에 로컬 CA를 설치하고, localhost용 인증서를 발급해서 서버에 붙입니다.&lt;/p&gt;
&lt;h4 data-end=&quot;410&quot; data-start=&quot;396&quot; data-ke-size=&quot;size20&quot;&gt;1) 인증서 발급&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;566&quot; data-start=&quot;411&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;484&quot; data-start=&quot;411&quot;&gt;mkcert 설치 후:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;484&quot; data-start=&quot;428&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;447&quot; data-start=&quot;428&quot;&gt;mkcert -install&lt;/li&gt;
&lt;li data-end=&quot;484&quot; data-start=&quot;450&quot;&gt;mkcert localhost 127.0.0.1 ::1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;566&quot; data-start=&quot;485&quot;&gt;그러면 대개 이런 파일이 생깁니다:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;566&quot; data-start=&quot;509&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;534&quot; data-start=&quot;509&quot;&gt;localhost+2.pem (인증서)&lt;/li&gt;
&lt;li data-end=&quot;566&quot; data-start=&quot;537&quot;&gt;localhost+2-key.pem (개인키)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;605&quot; data-start=&quot;568&quot; data-ke-size=&quot;size20&quot;&gt;2) Spring Boot(내장 톰캣)에 HTTPS 붙이기&lt;/h4&gt;
&lt;p data-end=&quot;666&quot; data-start=&quot;606&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot는 보통 &lt;b&gt;PKCS12(.p12) 키스토어&lt;/b&gt;를 씁니다. pem을 p12로 변환.&lt;/p&gt;
&lt;pre id=&quot;code_1769314223670&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;openssl pkcs12 -export \
  -in localhost+2.pem \
  -inkey localhost+2-key.pem \
  -out localhost.p12 \
  -name local-https \
  -passout pass:changeit&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src/main/resources/localhost.p12로 넣고 application.yml:&lt;/p&gt;
&lt;pre id=&quot;code_1769314240105&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  port: 8443
  ssl:
    enabled: true
    key-store: classpath:localhost.p12
    key-store-type: PKCS12
    key-store-password: changeit
    key-alias: local-https&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;1154&quot; data-start=&quot;1113&quot; data-ke-size=&quot;size20&quot;&gt;3) React(dev server)도 HTTPS로 띄우고 싶으면&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1275&quot; data-start=&quot;1155&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1209&quot; data-start=&quot;1155&quot;&gt;Vite/CRA 각각 방법이 다른데, 핵심은 &amp;ldquo;인증서/키를 지정해서 https로 구동&amp;rdquo;입니다.&lt;/li&gt;
&lt;li data-end=&quot;1275&quot; data-start=&quot;1210&quot;&gt;다만 보통은 &lt;b&gt;프론트는 http로 두고&lt;/b&gt;, API만 https로 붙이는 경우도 많아요(프록시 설정으로 해결).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1336&quot; data-start=&quot;1282&quot; data-ke-size=&quot;size23&quot;&gt;B. &amp;ldquo;로컬은 HTTP 그대로 + 프록시/리버스 프록시로만 HTTPS&amp;rdquo; (현업에서도 자주)&lt;/h3&gt;
&lt;p data-end=&quot;1419&quot; data-start=&quot;1337&quot; data-ke-size=&quot;size16&quot;&gt;로컬에서 HTTPS가 꼭 필요한 경우가 &lt;b&gt;쿠키 Secure, 일부 브라우저 기능(권한/보안 컨텍스트), OAuth 리다이렉트 URI&lt;/b&gt; 때문이면,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1503&quot; data-start=&quot;1420&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1441&quot; data-start=&quot;1420&quot;&gt;프론트/백엔드 자체는 http 유지&lt;/li&gt;
&lt;li data-end=&quot;1503&quot; data-start=&quot;1442&quot;&gt;앞단 프록시(예: nginx, caddy)에서만 https로 종단처리&lt;br /&gt;이 방식이 가장 덜 번거롭습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 배포후 https를 사용하는 법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1899&quot; data-start=&quot;1852&quot; data-ke-size=&quot;size23&quot;&gt;A. 가장 흔한 구조: Nginx(443) &amp;rarr; Spring Boot(8080)&lt;/h3&gt;
&lt;p data-end=&quot;1955&quot; data-start=&quot;1900&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt;: 운영 안정적, 인증서 갱신 자동화 쉬움, 리다이렉트/HSTS/압축/정적파일 처리 유리&lt;/p&gt;
&lt;h4 data-end=&quot;1971&quot; data-start=&quot;1957&quot; data-ke-size=&quot;size20&quot;&gt;1) DNS 설정&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2005&quot; data-start=&quot;1972&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2005&quot; data-start=&quot;1972&quot;&gt;도메인의 A 레코드(또는 CNAME)를 서버 IP로 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;2039&quot; data-start=&quot;2007&quot; data-ke-size=&quot;size20&quot;&gt;2) 인증서 발급 (Certbot + Nginx)&lt;/h4&gt;
&lt;p data-end=&quot;2055&quot; data-start=&quot;2040&quot; data-ke-size=&quot;size16&quot;&gt;Ubuntu 기준으로 보통:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2165&quot; data-start=&quot;2056&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2109&quot; data-start=&quot;2056&quot;&gt;certbot --nginx -d example.com -d www.example.com&lt;/li&gt;
&lt;li data-end=&quot;2165&quot; data-start=&quot;2110&quot;&gt;자동으로 Nginx 설정에 SSL 적용 + 갱신 스케줄(renew)까지 잡히는 경우가 많습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;2198&quot; data-start=&quot;2167&quot; data-ke-size=&quot;size20&quot;&gt;3) HTTP &amp;rarr; HTTPS 리다이렉트 (필수)&lt;/h4&gt;
&lt;p data-end=&quot;2224&quot; data-start=&quot;2199&quot; data-ke-size=&quot;size16&quot;&gt;Nginx에서 80은 무조건 443로 보내기:&lt;/p&gt;
&lt;pre id=&quot;code_1769314295683&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
  listen 80;
  server_name example.com www.example.com;
  return 301 https://$host$request_uri;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4) 443에서 프록시로 백엔드 연결&lt;/h4&gt;
&lt;pre id=&quot;code_1769314316700&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
  listen 443 ssl;
  server_name example.com www.example.com;

  ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  location /api/ {
    proxy_pass http://127.0.0.1:8080/;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }

  location / {
    # 리액트 정적 빌드 서빙 또는 별도 프론트 서버로 프록시
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-end=&quot;2982&quot; data-start=&quot;2929&quot; data-ke-size=&quot;size23&quot;&gt;B. 클라우드 쓰는 경우: 로드밸런서(ALB) / Cloudflare에서 HTTPS 종료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3140&quot; data-start=&quot;2983&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3033&quot; data-start=&quot;2983&quot;&gt;AWS ALB에 ACM 인증서 붙이고, ALB &amp;rarr; EC2(또는 ECS)로 HTTP 전달&lt;/li&gt;
&lt;li data-end=&quot;3140&quot; data-start=&quot;3034&quot;&gt;Cloudflare 사용 시 &amp;ldquo;Full(strict)&amp;rdquo; 등 모드로 종단 처리&lt;br /&gt;&lt;b&gt;포인트&lt;/b&gt;: 이 경우에도 &amp;ldquo;외부는 HTTPS&amp;rdquo;, 내부는 HTTP일 수 있습니다(내부망이면 보통 OK).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;3196&quot; data-start=&quot;3147&quot; data-ke-size=&quot;size23&quot;&gt;C. Spring Boot에 인증서 직접 붙여서 443로 띄우는 방식(가능은 함)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3268&quot; data-start=&quot;3197&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3218&quot; data-start=&quot;3197&quot;&gt;단독 서버/간단 배포면 가능하지만,&lt;/li&gt;
&lt;li data-end=&quot;3268&quot; data-start=&quot;3219&quot;&gt;운영에서는 보통 Nginx/ALB 앞단을 둡니다(갱신/보안헤더/성능/라우팅이 편함).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;3302&quot; data-start=&quot;3275&quot; data-ke-size=&quot;size26&quot;&gt;배포에서 꼭 같이 해야 하는 보안/동작 체크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3489&quot; data-start=&quot;3303&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3334&quot; data-start=&quot;3303&quot;&gt;&lt;b&gt;리다이렉트&lt;/b&gt;: 80 &amp;rarr; 443 (301/308)&lt;/li&gt;
&lt;li data-end=&quot;3379&quot; data-start=&quot;3335&quot;&gt;&lt;b&gt;HSTS&lt;/b&gt;(선택이지만 권장): HTTPS 강제(다운그레이드 공격 방지)&lt;/li&gt;
&lt;li data-end=&quot;3437&quot; data-start=&quot;3380&quot;&gt;&lt;b&gt;쿠키 설정&lt;/b&gt;: Secure, HttpOnly, SameSite (특히 세션/로그인)&lt;/li&gt;
&lt;li data-end=&quot;3489&quot; data-start=&quot;3438&quot;&gt;&lt;b&gt;혼합 콘텐츠 제거&lt;/b&gt;: 모든 리소스(API/이미지/CDN) URL을 https로 통일&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;https를 사용하여 추가 비용이 발생하는 경우&lt;/p&gt;
&lt;h3 data-end=&quot;353&quot; data-start=&quot;323&quot; data-ke-size=&quot;size23&quot;&gt;1) 로드밸런서(ALB)를 새로 쓰게 되는 경우&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;486&quot; data-start=&quot;354&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;399&quot; data-start=&quot;354&quot;&gt;단일 서버에 Nginx+Let&amp;rsquo;s Encrypt로 붙이면 &lt;b&gt;추가비용 없음&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;486&quot; data-start=&quot;400&quot;&gt;그런데 &amp;ldquo;HTTPS 처리를 ALB에서 하겠다&amp;rdquo;로 가면
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;486&quot; data-start=&quot;434&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;486&quot; data-start=&quot;434&quot;&gt;&lt;b&gt;ALB 사용료가 추가&lt;/b&gt;될 수 있음 (인증서는 ACM으로 무료여도 ALB 자체가 유료)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;515&quot; data-start=&quot;488&quot; data-ke-size=&quot;size23&quot;&gt;2) 유료 인증서(상용 CA)를 쓰는 경우&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;591&quot; data-start=&quot;516&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;551&quot; data-start=&quot;516&quot;&gt;회사 정책/특수 요구로 EV/OV 인증서 등을 요구하면 유료&lt;/li&gt;
&lt;li data-end=&quot;591&quot; data-start=&quot;552&quot;&gt;일반 개인/프로젝트/대부분 서비스는 Let&amp;rsquo;s Encrypt로 충분&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;627&quot; data-start=&quot;593&quot; data-ke-size=&quot;size23&quot;&gt;3) CDN/WAF/보안 서비스 유료 플랜을 쓰는 경우&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;710&quot; data-start=&quot;628&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;668&quot; data-start=&quot;628&quot;&gt;Cloudflare 같은 걸 &amp;ldquo;기능 때문에&amp;rdquo; 유료로 올릴 수는 있는데&lt;/li&gt;
&lt;li data-end=&quot;710&quot; data-start=&quot;669&quot;&gt;이것도 HTTPS 자체 때문이라기보단 &amp;ldquo;부가 기능&amp;rdquo; 때문에 비용이 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;82&quot; data-start=&quot;59&quot; data-ke-size=&quot;size26&quot;&gt;SSL 인증 (정확히는 TLS 인증)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;415&quot; data-start=&quot;83&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;122&quot; data-start=&quot;83&quot;&gt;&lt;b&gt;어디에 쓰나&lt;/b&gt;: 브라우저 &amp;harr; 웹서버, API 통신(HTTPS)&lt;/li&gt;
&lt;li data-end=&quot;221&quot; data-start=&quot;123&quot;&gt;&lt;b&gt;목적&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;221&quot; data-start=&quot;137&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;159&quot; data-start=&quot;137&quot;&gt;통신 &lt;b&gt;암호화&lt;/b&gt;(도청 방지)&lt;/li&gt;
&lt;li data-end=&quot;180&quot; data-start=&quot;162&quot;&gt;데이터 &lt;b&gt;변조 방지&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;221&quot; data-start=&quot;183&quot;&gt;&amp;ldquo;이 서버가 진짜 그 도메인의 서버인지&amp;rdquo; &lt;b&gt;서버 신원 확인&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li data-end=&quot;330&quot; data-start=&quot;222&quot;&gt;&lt;b&gt;신뢰의 근거&lt;/b&gt;: &lt;b&gt;CA(인증기관)&lt;/b&gt; 가 발급한 &lt;b&gt;인증서(서버의 신분증)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;330&quot; data-start=&quot;276&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;330&quot; data-start=&quot;276&quot;&gt;브라우저/OS는 &amp;ldquo;신뢰하는 CA 목록&amp;rdquo;을 기본으로 가지고 있고, 그 체인을 검증해서 믿습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;355&quot; data-start=&quot;331&quot;&gt;&lt;b&gt;대표 포트&lt;/b&gt;: 443 (HTTPS)&lt;/li&gt;
&lt;li data-end=&quot;415&quot; data-start=&quot;356&quot;&gt;&lt;b&gt;핵심 포인트&lt;/b&gt;: &amp;ldquo;서버가 &lt;b&gt;example.com&lt;/b&gt; 맞아?&amp;rdquo;를 공인 구조(CA)로 검증하는 체계&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;426&quot; data-start=&quot;417&quot; data-ke-size=&quot;size26&quot;&gt;SSH 인증&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;891&quot; data-start=&quot;427&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;486&quot; data-start=&quot;427&quot;&gt;&lt;b&gt;어디에 쓰나&lt;/b&gt;: 서버 원격 접속(터미널), 파일 전송(SCP/SFTP), Git 접속(git@&amp;hellip;)&lt;/li&gt;
&lt;li data-end=&quot;553&quot; data-start=&quot;487&quot;&gt;&lt;b&gt;목적&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;553&quot; data-start=&quot;501&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;522&quot; data-start=&quot;501&quot;&gt;원격 접속 통신 &lt;b&gt;암호화&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;553&quot; data-start=&quot;525&quot;&gt;접속하는 주체(사용자/클라이언트) &lt;b&gt;인증&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li data-end=&quot;822&quot; data-start=&quot;554&quot;&gt;&lt;b&gt;신뢰의 근거&lt;/b&gt;: 보통 &lt;b&gt;공개키/개인키 키페어&lt;/b&gt;(내가 만든 키)
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;822&quot; data-start=&quot;600&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;651&quot; data-start=&quot;600&quot;&gt;서버는 &amp;ldquo;이 사용자의 공개키를 허용한다&amp;rdquo;를 authorized_keys에 등록해둠&lt;/li&gt;
&lt;li data-end=&quot;685&quot; data-start=&quot;654&quot;&gt;클라이언트는 개인키로 &amp;ldquo;내가 그 키의 주인&amp;rdquo;임을 증명&lt;/li&gt;
&lt;li data-end=&quot;822&quot; data-start=&quot;688&quot;&gt;서버 자체의 신원도 확인은 하는데, 방식이 TLS와 달라요:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;822&quot; data-start=&quot;728&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;792&quot; data-start=&quot;728&quot;&gt;처음 접속 때 서버가 &lt;b&gt;host key&lt;/b&gt;를 보여주고, 클라이언트가 그 지문을 저장(known_hosts)&lt;/li&gt;
&lt;li data-end=&quot;822&quot; data-start=&quot;797&quot;&gt;이후 값이 바뀌면 경고(중간자 공격 의심)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;838&quot; data-start=&quot;823&quot;&gt;&lt;b&gt;대표 포트&lt;/b&gt;: 22&lt;/li&gt;
&lt;li data-end=&quot;891&quot; data-start=&quot;839&quot;&gt;&lt;b&gt;핵심 포인트&lt;/b&gt;: CA 같은 공인 구조보다는 &lt;b&gt;키 등록/지문 고정(TOFU)&lt;/b&gt; 중심&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;903&quot; data-start=&quot;893&quot; data-ke-size=&quot;size26&quot;&gt;한 줄로 비교&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1022&quot; data-start=&quot;904&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;968&quot; data-start=&quot;904&quot;&gt;&lt;b&gt;TLS(SSL)&lt;/b&gt;: &amp;ldquo;이 도메인의 서버가 진짜인지&amp;rdquo;를 &lt;b&gt;CA 인증서로&lt;/b&gt; 보증하면서 웹 통신을 안전하게.&lt;/li&gt;
&lt;li data-end=&quot;1022&quot; data-start=&quot;969&quot;&gt;&lt;b&gt;SSH&lt;/b&gt;: &amp;ldquo;서버에 접속하는 사용자가 누구인지&amp;rdquo;를 &lt;b&gt;키 기반 로그인&lt;/b&gt;으로 안전하게.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1088&quot; data-start=&quot;1043&quot;&gt;&lt;b&gt;HTTPS 배포&lt;/b&gt;: TLS 인증서(Let&amp;rsquo;s Encrypt 등) 필요&lt;/li&gt;
&lt;li data-end=&quot;1152&quot; data-start=&quot;1089&quot;&gt;&lt;b&gt;서버 배포/관리 접속&lt;/b&gt;: SSH 키 필요 (ssh ubuntu@서버IP, GitHub SSH 키 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS인증서를 발급 받을수 있는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;190&quot; data-start=&quot;155&quot; data-ke-size=&quot;size26&quot;&gt;1) Let&amp;rsquo;s Encrypt로 발급 (무료, 실무 표준)&lt;/h2&gt;
&lt;h3 data-end=&quot;199&quot; data-start=&quot;192&quot; data-ke-size=&quot;size23&quot;&gt;준비물&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;283&quot; data-start=&quot;200&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;246&quot; data-start=&quot;200&quot;&gt;도메인(예: example.com)이 내 서버 IP를 가리키도록 DNS 설정&lt;/li&gt;
&lt;li data-end=&quot;283&quot; data-start=&quot;247&quot;&gt;외부에서 80(HTTP) 또는 DNS 설정 변경이 가능해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;301&quot; data-start=&quot;285&quot; data-ke-size=&quot;size23&quot;&gt;인증(검증) 방식 2개&lt;/h3&gt;
&lt;h4 data-end=&quot;325&quot; data-start=&quot;302&quot; data-ke-size=&quot;size20&quot;&gt;A. HTTP-01 (가장 흔함)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;558&quot; data-start=&quot;326&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;440&quot; data-start=&quot;326&quot;&gt;Let&amp;rsquo;s Encrypt가 도메인이 정말 본인것 인지 확인하기 위해&lt;br /&gt;&lt;a href=&quot;http://example.com/.well-known/acme-challenge/...&quot;&gt;http://example.com/.well-known/acme-challenge/...&lt;/a&gt; 경로로 접근해 검증&lt;/li&gt;
&lt;li data-end=&quot;558&quot; data-start=&quot;441&quot;&gt;그래서 &lt;b&gt;80 포트가 외부에서 열려 있어야&lt;/b&gt; 합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;595&quot; data-start=&quot;560&quot; data-ke-size=&quot;size20&quot;&gt;B. DNS-01 (와일드카드/80 막힌 환경에 유리)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;770&quot; data-start=&quot;596&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;635&quot; data-start=&quot;596&quot;&gt;DNS에 _acme-challenge TXT 레코드를 넣어 검증&lt;/li&gt;
&lt;li data-end=&quot;770&quot; data-start=&quot;636&quot;&gt;&lt;b&gt;*.example.com 와일드카드 인증서&lt;/b&gt;가 필요하면 보통 이 방식이 필요합니다(일반적으로).&lt;br /&gt;(DNS-01은 TXT 레코드로 검증한다는 설명 참고. )&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;798&quot; data-start=&quot;777&quot; data-ke-size=&quot;size23&quot;&gt;발급 도구(ACME 클라이언트)&lt;/h3&gt;
&lt;h4 data-end=&quot;823&quot; data-start=&quot;799&quot; data-ke-size=&quot;size20&quot;&gt;A. Certbot (가장 대중적)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1000&quot; data-start=&quot;824&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;913&quot; data-start=&quot;824&quot;&gt;Nginx/Apache 플러그인으로 &amp;ldquo;발급 + 설정&amp;rdquo;까지 자동으로 해주는 경우가 많아요.&lt;/li&gt;
&lt;li data-end=&quot;1000&quot; data-start=&quot;914&quot;&gt;갱신도 certbot renew로 자동화가 잘 되어 있습니다(드라이런도 지원).&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;1039&quot; data-start=&quot;1002&quot; data-ke-size=&quot;size20&quot;&gt;B. acme.sh (가볍고, DNS-01 자동화에 강함)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1133&quot; data-start=&quot;1040&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1133&quot; data-start=&quot;1040&quot;&gt;임베디드/공유기/도커 등에서도 많이 씁니다(standalone/webroot/dns 등 모드).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1189&quot; data-start=&quot;1140&quot; data-ke-size=&quot;size26&quot;&gt;2) AWS 같은 클라우드에서 &amp;ldquo;관리형 인증서&amp;rdquo;로 발급 (대개 무료지만 제약 있음)&lt;/h2&gt;
&lt;h3 data-end=&quot;1231&quot; data-start=&quot;1191&quot; data-ke-size=&quot;size23&quot;&gt;AWS ACM (Amazon Certificate Manager)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1489&quot; data-start=&quot;1232&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1375&quot; data-start=&quot;1232&quot;&gt;&lt;b&gt;ACM에서 퍼블릭 인증서를 요청&lt;/b&gt;할 수 있고,&lt;br /&gt;CloudFront/ELB/API Gateway 같은 &lt;b&gt;AWS 통합 서비스에 붙이면 갱신까지 관리형&lt;/b&gt;으로 굴릴 수 있어요.&lt;/li&gt;
&lt;li data-end=&quot;1489&quot; data-start=&quot;1376&quot;&gt;단, 일반적으로 &amp;ldquo;EC2에 파일로 내려받아 설치&amp;rdquo; 같은 건 제약이 있습니다(통합 서비스용으로 쓰는 형태).&lt;br /&gt;(그래서 EC2 단독이면 보통 Let&amp;rsquo;s Encrypt + Nginx로 처리합니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1538&quot; data-start=&quot;1496&quot; data-ke-size=&quot;size26&quot;&gt;3) 자체 서명(Self-signed) 인증서 (무료, 로컬/내부망용)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1618&quot; data-start=&quot;1539&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1554&quot; data-start=&quot;1539&quot;&gt;내가 직접 서명한 인증서&lt;/li&gt;
&lt;li data-end=&quot;1618&quot; data-start=&quot;1555&quot;&gt;&lt;b&gt;브라우저에 &amp;lsquo;안전하지 않음&amp;rsquo; 경고&lt;/b&gt;가 뜨는 게 정상이라, 보통 &lt;b&gt;로컬 개발/내부 테스트&lt;/b&gt;에만 씁니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http의 버전별 차이점과 성능&lt;/p&gt;
&lt;h2 data-end=&quot;70&quot; data-start=&quot;59&quot; data-ke-size=&quot;size26&quot;&gt;HTTP/0.9&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;148&quot; data-start=&quot;71&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;116&quot; data-start=&quot;71&quot;&gt;아주 초기 형태. &lt;b&gt;GET만&lt;/b&gt; 있고 헤더/상태코드 같은 개념이 거의 없음.&lt;/li&gt;
&lt;li data-end=&quot;148&quot; data-start=&quot;117&quot;&gt;성능/기능 비교 대상으로는 거의 안 씀(역사 수준).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;166&quot; data-start=&quot;155&quot; data-ke-size=&quot;size26&quot;&gt;HTTP/1.0&lt;/h2&gt;
&lt;p data-end=&quot;173&quot; data-start=&quot;167&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;246&quot; data-start=&quot;174&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;208&quot; data-start=&quot;174&quot;&gt;요청/응답에 &lt;b&gt;헤더&lt;/b&gt; 개념이 생기고, 상태코드도 정착.&lt;/li&gt;
&lt;li data-end=&quot;246&quot; data-start=&quot;209&quot;&gt;기본적으로 &lt;b&gt;요청 1번 = TCP 연결 1번&lt;/b&gt;(짧게 끊김).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;254&quot; data-start=&quot;248&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성능&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;337&quot; data-start=&quot;255&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;303&quot; data-start=&quot;255&quot;&gt;리소스(이미지/CSS/JS)가 많은 웹페이지는 연결을 계속 새로 열어야 해서 느림.&lt;/li&gt;
&lt;li data-end=&quot;337&quot; data-start=&quot;304&quot;&gt;TCP 핸드셰이크(3-way) 비용이 매 요청마다 반복.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;355&quot; data-start=&quot;344&quot; data-ke-size=&quot;size26&quot;&gt;HTTP/1.1&lt;/h2&gt;
&lt;p data-end=&quot;365&quot; data-start=&quot;356&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 개선&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;519&quot; data-start=&quot;366&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;403&quot; data-start=&quot;366&quot;&gt;&lt;b&gt;지속 연결(Keep-Alive)&lt;/b&gt;: TCP 연결을 재사용.&lt;/li&gt;
&lt;li data-end=&quot;477&quot; data-start=&quot;404&quot;&gt;&lt;b&gt;파이프라이닝(pipelining)&lt;/b&gt;: 여러 요청을 연달아 보내는 시도(하지만 실전에서는 문제 때문에 거의 비활성/비권장).&lt;/li&gt;
&lt;li data-end=&quot;519&quot; data-start=&quot;478&quot;&gt;&lt;b&gt;Host 헤더&lt;/b&gt;로 가상호스팅 가능(하나의 IP에서 여러 도메인).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;530&quot; data-start=&quot;521&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성능 특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;613&quot; data-start=&quot;531&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;554&quot; data-start=&quot;531&quot;&gt;1.0보다 훨씬 좋아짐(연결 재사용).&lt;/li&gt;
&lt;li data-end=&quot;613&quot; data-start=&quot;555&quot;&gt;하지만 여전히 &amp;ldquo;한 연결에서 요청/응답이 사실상 &lt;b&gt;순서대로&lt;/b&gt;&amp;rdquo; 처리되는 성격이 강해 병목이 생김.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;664&quot; data-start=&quot;615&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;대표 병목: HOL(Head-of-Line) Blocking (애플리케이션 레벨)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;783&quot; data-start=&quot;665&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;706&quot; data-start=&quot;665&quot;&gt;하나의 큰 응답이 앞에 걸리면 뒤의 작은 파일들이 &lt;b&gt;줄줄이 대기&lt;/b&gt;.&lt;/li&gt;
&lt;li data-end=&quot;783&quot; data-start=&quot;707&quot;&gt;그래서 브라우저가 &amp;ldquo;병렬 다운로드&amp;rdquo;하려고 &lt;b&gt;여러 TCP 연결을 동시에&lt;/b&gt; 여는 방식으로 우회(도메인당 6개 내외 같은 제한 관행).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;799&quot; data-start=&quot;790&quot; data-ke-size=&quot;size26&quot;&gt;HTTP/2&lt;/h2&gt;
&lt;p data-end=&quot;831&quot; data-start=&quot;800&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 목표: &amp;ldquo;한 연결에서 진짜 병렬로 처리하자&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1033&quot; data-start=&quot;832&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;872&quot; data-start=&quot;832&quot;&gt;&lt;b&gt;바이너리 프레이밍&lt;/b&gt;: 텍스트가 아니라 프레임 단위로 쪼개 전송.&lt;/li&gt;
&lt;li data-end=&quot;929&quot; data-start=&quot;873&quot;&gt;&lt;b&gt;멀티플렉싱&lt;/b&gt;: &lt;b&gt;하나의 TCP 연결&lt;/b&gt; 위에서 여러 요청/응답을 동시에 섞어 보내기 가능.&lt;/li&gt;
&lt;li data-end=&quot;967&quot; data-start=&quot;930&quot;&gt;&lt;b&gt;헤더 압축(HPACK)&lt;/b&gt;: 중복 헤더를 줄여 전송량 감소.&lt;/li&gt;
&lt;li data-end=&quot;1033&quot; data-start=&quot;968&quot;&gt;&lt;b&gt;서버 푸시(Server Push)&lt;/b&gt;: 서버가 필요 리소스를 미리 푸시(요즘은 운영에서 꺼두는 경우가 많음).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1050&quot; data-start=&quot;1035&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성능이 좋아지는 이유&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1186&quot; data-start=&quot;1051&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1096&quot; data-start=&quot;1051&quot;&gt;HTTP/1.1의 핵심 병목이던 &lt;b&gt;애플리케이션 레벨 HOL&lt;/b&gt;을 크게 줄임.&lt;/li&gt;
&lt;li data-end=&quot;1151&quot; data-start=&quot;1097&quot;&gt;여러 연결을 억지로 열 필요가 줄어 &lt;b&gt;TCP 연결 수 감소&lt;/b&gt; &amp;rarr; 서버/네트워크 효율 증가.&lt;/li&gt;
&lt;li data-end=&quot;1186&quot; data-start=&quot;1152&quot;&gt;헤더가 큰 API 환경에서 HPACK이 체감될 때가 많음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1213&quot; data-start=&quot;1188&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;하지만 남는 병목: TCP 레벨 HOL&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1332&quot; data-start=&quot;1214&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1243&quot; data-start=&quot;1214&quot;&gt;HTTP/2는 여전히 &lt;b&gt;TCP 위에서 동작&lt;/b&gt;.&lt;/li&gt;
&lt;li data-end=&quot;1332&quot; data-start=&quot;1244&quot;&gt;TCP는 패킷 손실이 나면 그 이후 데이터도 &amp;ldquo;정렬/재전송&amp;rdquo; 때문에 대기 &amp;rarr; &lt;b&gt;한 패킷 손실이 전체 스트림에 영향&lt;/b&gt;을 줄 수 있음(특히 모바일/무선).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1358&quot; data-start=&quot;1339&quot; data-ke-size=&quot;size26&quot;&gt;HTTP/3 (QUIC 기반)&lt;/h2&gt;
&lt;p data-end=&quot;1392&quot; data-start=&quot;1359&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 목표: &amp;ldquo;TCP 때문에 생기는 HOL도 없애자&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1476&quot; data-start=&quot;1393&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1438&quot; data-start=&quot;1393&quot;&gt;HTTP/3는 TCP가 아니라 &lt;b&gt;QUIC(UDP 위에서 동작)&lt;/b&gt; 를 사용.&lt;/li&gt;
&lt;li data-end=&quot;1476&quot; data-start=&quot;1439&quot;&gt;QUIC은 내부적으로 암호화(TLS 1.3)와 전송제어를 포함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1492&quot; data-start=&quot;1478&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 개선점(성능)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1711&quot; data-start=&quot;1493&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1556&quot; data-start=&quot;1493&quot;&gt;&lt;b&gt;스트림 단위 전송&lt;/b&gt;: 한 스트림에서 손실이 나도 다른 스트림은 영향이 훨씬 적음 &amp;rarr; TCP HOL 완화.&lt;/li&gt;
&lt;li data-end=&quot;1605&quot; data-start=&quot;1557&quot;&gt;&lt;b&gt;연결 성립이 빠름&lt;/b&gt;: 보통 TLS 1.3과 결합하여 RTT를 줄이는 데 유리.&lt;/li&gt;
&lt;li data-end=&quot;1673&quot; data-start=&quot;1606&quot;&gt;&lt;b&gt;Connection Migration&lt;/b&gt;: IP가 바뀌어도(와이파이&amp;harr;LTE) 연결을 더 잘 이어감(모바일 강점).&lt;/li&gt;
&lt;li data-end=&quot;1711&quot; data-start=&quot;1674&quot;&gt;손실 회복/혼잡제어가 애플리케이션 레벨에서 더 유연하게 설계됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1725&quot; data-start=&quot;1713&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;체감이 큰 환경&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1770&quot; data-start=&quot;1726&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1754&quot; data-start=&quot;1726&quot;&gt;모바일, 무선, 패킷 손실/지연이 있는 네트워크&lt;/li&gt;
&lt;li data-end=&quot;1770&quot; data-start=&quot;1755&quot;&gt;리소스/요청이 많은 웹앱&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1831&quot; data-start=&quot;1795&quot;&gt;&lt;b&gt;1.0 &amp;rarr; 1.1&lt;/b&gt;: 연결 재사용으로 기본 성능 개선&lt;/li&gt;
&lt;li data-end=&quot;1881&quot; data-start=&quot;1832&quot;&gt;&lt;b&gt;1.1 &amp;rarr; 2&lt;/b&gt;: 한 연결에서 멀티플렉싱 + 헤더 압축으로 병렬성/효율 개선&lt;/li&gt;
&lt;li data-end=&quot;1931&quot; data-start=&quot;1882&quot;&gt;&lt;b&gt;2 &amp;rarr; 3&lt;/b&gt;: TCP HOL을 QUIC으로 줄여 특히 불안정한 네트워크에서 강함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;https는 버전이 없는지?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;154&quot; data-start=&quot;53&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;94&quot; data-start=&quot;53&quot;&gt;&lt;b&gt;HTTP 버전&lt;/b&gt;: HTTP/1.1, HTTP/2, HTTP/3&lt;/li&gt;
&lt;li data-end=&quot;154&quot; data-start=&quot;95&quot;&gt;&lt;b&gt;TLS 버전(= HTTPS에서의 보안 프로토콜 버전)&lt;/b&gt;: TLS 1.0, 1.1, 1.2, 1.3&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;226&quot; data-start=&quot;156&quot; data-ke-size=&quot;size16&quot;&gt;즉 &lt;b&gt;HTTPS = (어떤 HTTP 버전) + TLS&lt;/b&gt; 조합이고, &amp;ldquo;HTTPS 2.0&amp;rdquo; 같은 식으로 말하진 않는 편.&lt;/p&gt;
&lt;p data-end=&quot;226&quot; data-start=&quot;156&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;280&quot; data-start=&quot;233&quot; data-ke-size=&quot;size26&quot;&gt;그럼 HTTP/2에서 HTTPS vs HTTP/3에서 HTTPS는 뭐가 다른지?&lt;/h2&gt;
&lt;p data-end=&quot;314&quot; data-start=&quot;282&quot; data-ke-size=&quot;size16&quot;&gt;둘 다 &amp;ldquo;암호화된 웹 통신&amp;rdquo;이지만 &lt;b&gt;아래가 다릅니다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-end=&quot;336&quot; data-start=&quot;316&quot; data-ke-size=&quot;size23&quot;&gt;1) 밑바닥 전송 계층이 다름&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;514&quot; data-start=&quot;337&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;395&quot; data-start=&quot;337&quot;&gt;&lt;b&gt;HTTP/2 over HTTPS&lt;/b&gt;: HTTP/2 + TLS 가 &lt;b&gt;TCP 위에서&lt;/b&gt; 동작&lt;/li&gt;
&lt;li data-end=&quot;514&quot; data-start=&quot;396&quot;&gt;&lt;b&gt;HTTP/3 over HTTPS&lt;/b&gt;: HTTP/3 + TLS 1.3 이 &lt;b&gt;QUIC(UDP 기반) 위에서&lt;/b&gt; 동작
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;514&quot; data-start=&quot;470&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;514&quot; data-start=&quot;470&quot;&gt;QUIC 안에 TLS 1.3 핸드셰이크가 &lt;b&gt;통합&lt;/b&gt;돼 있다고 보면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;567&quot; data-start=&quot;516&quot; data-ke-size=&quot;size16&quot;&gt;결과적으로 &amp;ldquo;HTTPS를 쓰느냐&amp;rdquo;보다, &lt;b&gt;TCP냐 QUIC이냐&lt;/b&gt;가 체감 차이를 만듭니다.&lt;/p&gt;
&lt;p data-end=&quot;567&quot; data-start=&quot;516&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;640&quot; data-start=&quot;599&quot; data-ke-size=&quot;size16&quot;&gt;**HTTP/2(=TCP 기반)**는 &amp;ldquo;한 연결에서 멀티플렉싱&amp;rdquo;은 되지만,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;719&quot; data-start=&quot;641&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;719&quot; data-start=&quot;641&quot;&gt;TCP 특성상 패킷 손실이 나면 그 연결의 데이터가 대기하는 &lt;b&gt;TCP 레벨 HOL(Head-of-Line)&lt;/b&gt; 문제가 생길 수 있어요.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;760&quot; data-start=&quot;721&quot; data-ke-size=&quot;size16&quot;&gt;**HTTP/3(=QUIC 기반)**는 스트림을 더 분리해서 다루므로,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;838&quot; data-start=&quot;761&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;797&quot; data-start=&quot;761&quot;&gt;한 스트림에서 손실이 나도 다른 스트림 영향이 상대적으로 적고&lt;/li&gt;
&lt;li data-end=&quot;838&quot; data-start=&quot;798&quot;&gt;특히 &lt;b&gt;모바일/무선/손실 많은 환경&lt;/b&gt;에서 유리한 경우가 많습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;142&quot; data-start=&quot;60&quot;&gt;&lt;b&gt;HTTP/2&lt;/b&gt;: 표준상 &lt;b&gt;TLS 없이도 가능(h2c)&lt;/b&gt; 하지만, &lt;b&gt;브라우저 웹에선 사실상 HTTPS(h2)만 쓴다&lt;/b&gt;고 봐도 됨.&lt;/li&gt;
&lt;li data-end=&quot;211&quot; data-start=&quot;143&quot;&gt;&lt;b&gt;HTTP/3&lt;/b&gt;: 구조상 &lt;b&gt;TLS 1.3이 사실상 필수&lt;/b&gt;라서 브라우저 환경에선 &lt;b&gt;항상 암호화&lt;/b&gt;라고 보면 됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;TLS의 버전별 차이는?&lt;/b&gt;&lt;/h3&gt;
&lt;h2 data-end=&quot;209&quot; data-start=&quot;175&quot; data-ke-size=&quot;size26&quot;&gt;TLS 1.0 (1999) / TLS 1.1 (2006)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;453&quot; data-start=&quot;210&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;318&quot; data-start=&quot;210&quot;&gt;&lt;b&gt;현황&lt;/b&gt;: IETF에서 &lt;b&gt;공식적으로 폐기(Historic)&lt;/b&gt; 되었고, 산업 표준/브라우저도 대부분 비활성화했습니다.&lt;/li&gt;
&lt;li data-end=&quot;453&quot; data-start=&quot;319&quot;&gt;&lt;b&gt;왜 위험한가(요지)&lt;/b&gt;: 최신 권고 암호/메커니즘 지원이 부족하고, 현대 보안 프로파일에서 요구하는 수준을 충족하기 어렵습니다(그래서 &amp;ldquo;피해야 한다&amp;rdquo;가 표준 결론).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 신규 구축/배포에서 TLS1.0/1.1은 옵션에서 빼는게 원칙&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;526&quot; data-start=&quot;509&quot; data-ke-size=&quot;size26&quot;&gt;TLS 1.2 (2008)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;934&quot; data-start=&quot;527&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;620&quot; data-start=&quot;527&quot;&gt;&lt;b&gt;현황&lt;/b&gt;: 아직도 가장 널리 호환되는 &amp;ldquo;현역&amp;rdquo; 버전(레거시 호환용으로 유지되는 경우 많음).&lt;/li&gt;
&lt;li data-end=&quot;777&quot; data-start=&quot;621&quot;&gt;&lt;b&gt;핵심 개선 방향&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;777&quot; data-start=&quot;638&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;777&quot; data-start=&quot;638&quot;&gt;암호 스위트 구성/해시/서명 알고리즘 선택 폭이 커지고, 현대적인 조합(AEAD 등)을 사용할 수 있는 기반이 됩니다. (표준은 TLS 1.2가 1.0/1.1을 대체했다고 명시)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;934&quot; data-start=&quot;778&quot;&gt;&lt;b&gt;주의점(중요)&lt;/b&gt;: TLS 1.2는 &amp;ldquo;구식 알고리즘도 여전히 선택 가능&amp;rdquo;한 구조라서, &lt;b&gt;서버 설정이 허술하면 약한 조합으로 협상될 수 있음&lt;/b&gt; &amp;rarr; 그래서 보안 가이드는 강한 조합만 허용하도록 권장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;958&quot; data-start=&quot;941&quot; data-ke-size=&quot;size26&quot;&gt;TLS 1.3 (2018)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1124&quot; data-start=&quot;959&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1106&quot; data-start=&quot;959&quot;&gt;&lt;b&gt;현황&lt;/b&gt;: 최신 표준. 보안/단순성/성능을 위해 &lt;b&gt;프로토콜을 크게 정리&lt;/b&gt;했습니다. TLS 1.3 문서는 TLS 1.2(RFC 5246)를 &amp;ldquo;폐기/대체(obsoletes)&amp;rdquo;로 명시합니다.&lt;/li&gt;
&lt;li data-end=&quot;1124&quot; data-start=&quot;1107&quot;&gt;&lt;b&gt;가장 큰 차이 3가지&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1150&quot; data-start=&quot;1126&quot; data-ke-size=&quot;size23&quot;&gt;1) 더 빠른 핸드셰이크(지연 감소)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1291&quot; data-start=&quot;1151&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1291&quot; data-start=&quot;1151&quot;&gt;일반적으로 &lt;b&gt;1-RTT&lt;/b&gt;로 핸드셰이크가 끝나도록 줄였고, 재접속 상황에선 &lt;b&gt;0-RTT(early data)&lt;/b&gt; 도 가능하지만 &lt;b&gt;리플레이 위험&lt;/b&gt;이 있어 신중히 써야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1322&quot; data-start=&quot;1293&quot; data-ke-size=&quot;size23&quot;&gt;2) &amp;ldquo;안전한 것만 남기기&amp;rdquo;로 프로토콜 단순화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1437&quot; data-start=&quot;1323&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1437&quot; data-start=&quot;1323&quot;&gt;TLS 1.3는 구식 구성요소를 대거 제거해서, 운영자가 &amp;ldquo;실수로 약한 조합을 켜는&amp;rdquo; 가능성을 줄였습니다(요지는 &lt;b&gt;단순+강화&lt;/b&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1467&quot; data-start=&quot;1439&quot; data-ke-size=&quot;size23&quot;&gt;3) 더 강한 기본 보안 성질(현대적 설계)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1573&quot; data-start=&quot;1468&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1573&quot; data-start=&quot;1468&quot;&gt;최신 권고에 맞춰 TLS 1.3을 기본으로 두고, 호환 필요 시에만 TLS 1.2를 허용하라는 가이드가 일반적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HSTS란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP Strict Transport Security :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;이 도메인은 앞으로 무조건 HTTPS로만 접속해라&amp;rdquo;&lt;/b&gt; 를 브라우저에 강제시키는 보안 정책&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;125&quot; data-start=&quot;118&quot; data-ke-size=&quot;size26&quot;&gt;왜 쓰나&lt;/h2&gt;
&lt;p data-end=&quot;298&quot; data-start=&quot;126&quot; data-ke-size=&quot;size16&quot;&gt;HTTP&amp;rarr;HTTPS로 리다이렉트만 해도 보통은 되지만, 공격자가 중간에서 &lt;b&gt;HTTPS로 가는 리다이렉트를 막고 HTTP에 머물게 하는 공격(SSL Stripping)&lt;/b&gt; 을 시도할 수 있어요.&lt;br /&gt;HSTS가 있으면 브라우저가 &lt;b&gt;아예 HTTP로 접속 자체를 시도하지 않고&lt;/b&gt; 처음부터 HTTPS로 붙습니다.&lt;/p&gt;
&lt;h2 data-end=&quot;311&quot; data-start=&quot;300&quot; data-ke-size=&quot;size26&quot;&gt;어떻게 동작하나&lt;/h2&gt;
&lt;p data-end=&quot;341&quot; data-start=&quot;312&quot; data-ke-size=&quot;size16&quot;&gt;서버가 HTTPS 응답에 아래 헤더를 실어 보냅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;413&quot; data-start=&quot;343&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;413&quot; data-start=&quot;343&quot;&gt;Strict-Transport-Security: max-age=...; includeSubDomains; preload&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;430&quot; data-start=&quot;415&quot; data-ke-size=&quot;size16&quot;&gt;브라우저는 이걸 기억했다가:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;512&quot; data-start=&quot;431&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;464&quot; data-start=&quot;431&quot;&gt;사용자가 &lt;a href=&quot;http://example.com&quot;&gt;http://example.com&lt;/a&gt;을 입력해도&lt;/li&gt;
&lt;li data-end=&quot;512&quot; data-start=&quot;465&quot;&gt;브라우저가 내부적으로 &lt;a href=&quot;https://example.com&quot;&gt;https://example.com&lt;/a&gt;으로 바꿔 접속합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;522&quot; data-start=&quot;514&quot; data-ke-size=&quot;size26&quot;&gt;주요 옵션&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;739&quot; data-start=&quot;523&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;599&quot; data-start=&quot;523&quot;&gt;&lt;b&gt;max-age&lt;/b&gt;: &amp;ldquo;얼마나 오래 HTTPS만 쓰도록 강제할지&amp;rdquo;(초 단위)
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;599&quot; data-start=&quot;571&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;599&quot; data-start=&quot;571&quot;&gt;예: max-age=31536000 (1년)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;685&quot; data-start=&quot;600&quot;&gt;&lt;b&gt;includeSubDomains&lt;/b&gt;: 하위 도메인도 전부 강제
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;685&quot; data-start=&quot;641&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;685&quot; data-start=&quot;641&quot;&gt;예: api.example.com, www.example.com 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;739&quot; data-start=&quot;686&quot;&gt;&lt;b&gt;preload&lt;/b&gt;: 브라우저에 &amp;ldquo;처음부터&amp;rdquo; 강제 목록으로 등록하는 용도(아래 주의 참고)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;752&quot; data-start=&quot;741&quot; data-ke-size=&quot;size26&quot;&gt;중요한 주의사항&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;983&quot; data-start=&quot;753&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;869&quot; data-start=&quot;753&quot;&gt;HSTS는 &lt;b&gt;한 번 적용하면 되돌리기 어렵습니다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;869&quot; data-start=&quot;788&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;869&quot; data-start=&quot;788&quot;&gt;특히 includeSubDomains + max-age가 길면, 하위 도메인까지 HTTPS가 준비되지 않았을 때 장애가 날 수 있어요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;983&quot; data-start=&quot;870&quot;&gt;&lt;b&gt;preload&lt;/b&gt;까지 등록하면(브라우저 내장 목록) 사실상 &amp;ldquo;되돌리기 더 어려운&amp;rdquo; 수준입니다.&lt;br /&gt;그래서 preload는 &lt;b&gt;모든 서브도메인까지 HTTPS 완비&lt;/b&gt;된 뒤에 하는 게 안전합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daily Dev Q&amp;amp;A</category>
      <author>awake123</author>
      <guid isPermaLink="true">https://awake123.tistory.com/63</guid>
      <comments>https://awake123.tistory.com/63#entry63comment</comments>
      <pubDate>Sun, 25 Jan 2026 12:08:44 +0900</pubDate>
    </item>
    <item>
      <title>JVM</title>
      <link>https://awake123.tistory.com/60</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;자바&amp;nbsp;가상&amp;nbsp;머신 &lt;br /&gt;컴파일된&amp;nbsp;.class&amp;nbsp;파일(바이트코드)을&amp;nbsp;os관계&amp;nbsp;없이&amp;nbsp;실행시켜주는&amp;nbsp;도구 &lt;br /&gt;&lt;br /&gt;실무에서의&amp;nbsp;중요도 &lt;br /&gt;사용하지&amp;nbsp;못한다면&amp;nbsp;:&amp;nbsp; &lt;br /&gt;&lt;br /&gt;자바&amp;nbsp;바이트&amp;nbsp;코드&amp;nbsp;실행&amp;nbsp;불가.&amp;nbsp;-&amp;gt;&amp;nbsp;모든&amp;nbsp;자바기반&amp;nbsp;서비스&amp;nbsp;동작&amp;nbsp;불가. &lt;br /&gt;플랫폼&amp;nbsp;독립성&amp;nbsp;상실&amp;nbsp;-&amp;gt;&amp;nbsp;윈도우,&amp;nbsp;리눅스&amp;nbsp;,&amp;nbsp;맥에&amp;nbsp;맞게&amp;nbsp;매번&amp;nbsp;빌드해야함. &lt;br /&gt;가비지&amp;nbsp;컬렉션&amp;nbsp;기반&amp;nbsp;메모리&amp;nbsp;관리&amp;nbsp;기능&amp;nbsp;소실&amp;nbsp;-&amp;gt;&amp;nbsp;메모리&amp;nbsp;누수&amp;nbsp;위험&amp;nbsp;증가 &lt;br /&gt;자바&amp;nbsp;생태계&amp;nbsp;라이브러리,&amp;nbsp;프레임워크&amp;nbsp;사용&amp;nbsp;불가&amp;nbsp;-&amp;gt;&amp;nbsp;스프링기반&amp;nbsp;웹&amp;nbsp;서비스&amp;nbsp;구동&amp;nbsp;불가&amp;nbsp;/&amp;nbsp;마이크로서비스&amp;nbsp;기반&amp;nbsp;구조&amp;nbsp;대부분&amp;nbsp;장애. &lt;br /&gt;개발&amp;nbsp;운영&amp;nbsp;생산성&amp;nbsp;급락&amp;nbsp;-&amp;gt;&amp;nbsp;JIT&amp;nbsp;최적화&amp;nbsp;,&amp;nbsp;여러&amp;nbsp;성능의&amp;nbsp;튜닝&amp;nbsp;옵션,&amp;nbsp;풍부한&amp;nbsp;모니터링도구(JMX,Flight&amp;nbsp;Recorder)등&amp;nbsp;사라짐. &lt;br /&gt;&lt;br /&gt;금융권&amp;nbsp;백엔드&amp;nbsp;시스템,&amp;nbsp;공공기관&amp;nbsp;전산시스템,&amp;nbsp;대규모&amp;nbsp;웹서비스&amp;nbsp;등도&amp;nbsp;자바&amp;nbsp;기반이기에&amp;nbsp;사용불가. &lt;br /&gt;&lt;br /&gt;최적화/효율성&amp;nbsp;제공 &lt;br /&gt;최적화&amp;nbsp;: &lt;br /&gt;&lt;br /&gt;JIT&amp;nbsp;컴파일&amp;nbsp;-&amp;gt;&amp;nbsp;jvm은&amp;nbsp;처음에는&amp;nbsp;바이트&amp;nbsp;코드를&amp;nbsp;인터프리터로&amp;nbsp;실행시키지만&amp;nbsp;자주&amp;nbsp;실행되는&amp;nbsp;코드를&amp;nbsp;감지하면&amp;nbsp;네이티브&amp;nbsp;코드로&amp;nbsp;컴파일하여&amp;nbsp;속도를&amp;nbsp;향상시킨다. &lt;br /&gt;hotspot기반&amp;nbsp;실행&amp;nbsp;최적화&amp;nbsp;-&amp;gt;&amp;nbsp;코드&amp;nbsp;실행&amp;nbsp;패턴을&amp;nbsp;관찰하여&amp;nbsp;가장&amp;nbsp;많이&amp;nbsp;사용되는&amp;nbsp;부분(hot&amp;nbsp;spot)을&amp;nbsp;자동&amp;nbsp;최적화.&amp;nbsp;/&amp;nbsp;개발자가&amp;nbsp;성능&amp;nbsp;튜닝을&amp;nbsp;하지&amp;nbsp;않아도&amp;nbsp;지속적으로&amp;nbsp;개선. &lt;br /&gt;가비지&amp;nbsp;컬렉션&amp;nbsp;기반&amp;nbsp;메모리&amp;nbsp;최적화&amp;nbsp;-&amp;gt;&amp;nbsp;필요없는&amp;nbsp;객체를&amp;nbsp;제거하여&amp;nbsp;메모리&amp;nbsp;누수방지,&amp;nbsp;서버&amp;nbsp;안정성&amp;nbsp;향상,&amp;nbsp;등의&amp;nbsp;효과를&amp;nbsp;제공. &lt;br /&gt;멀티스레드&amp;nbsp;최적화&amp;nbsp;-&amp;gt;&amp;nbsp;스레드&amp;nbsp;스케쥴링,&amp;nbsp;&amp;nbsp;락&amp;nbsp;최적화,&amp;nbsp;병렬&amp;nbsp;가비지&amp;nbsp;컬렉션&amp;nbsp;으로&amp;nbsp;멀티코어&amp;nbsp;자원&amp;nbsp;최대&amp;nbsp;활용. &lt;br /&gt;&lt;br /&gt;효율성&amp;nbsp;: &lt;br /&gt;플랫폼&amp;nbsp;독립성&amp;nbsp;-&amp;gt;&amp;nbsp;바이트&amp;nbsp;코드는&amp;nbsp;어느&amp;nbsp;os에서나&amp;nbsp;자바&amp;nbsp;가상머신으로&amp;nbsp;동일하게&amp;nbsp;실행.&amp;nbsp;/&amp;nbsp;운영&amp;nbsp;비용&amp;nbsp;절감 &lt;br /&gt;메모리&amp;nbsp;영역&amp;nbsp;최적화&amp;nbsp;구조&amp;nbsp;-&amp;gt;&amp;nbsp;Heap&amp;nbsp;객체&amp;nbsp;저장&amp;nbsp;/&amp;nbsp;Stack&amp;nbsp;메서드&amp;nbsp;호출,&amp;nbsp;지역변수&amp;nbsp;/&amp;nbsp;Method&amp;nbsp;Area&amp;nbsp;클래스&amp;nbsp;정보&amp;nbsp;/&amp;nbsp;Native&amp;nbsp;Method&amp;nbsp;Stack&amp;nbsp;JNI&amp;nbsp;호출 &lt;br /&gt;라이브러리/프레임워크&amp;nbsp;생태계&amp;nbsp;활용&amp;nbsp;-&amp;gt;&amp;nbsp;자바,&amp;nbsp;코틀린,&amp;nbsp;스칼라,&amp;nbsp;그루비등&amp;nbsp;언어모두&amp;nbsp;동작가능&amp;nbsp;/&amp;nbsp;Spring&amp;nbsp;,&amp;nbsp;Hibernate&amp;nbsp;,&amp;nbsp;Spark,&amp;nbsp;Netty등&amp;nbsp;강력한&amp;nbsp;프레임워크 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;자바&amp;nbsp;가상머신의&amp;nbsp;주요&amp;nbsp;역할&amp;nbsp;:&amp;nbsp; &lt;br /&gt;바이트&amp;nbsp;코드를&amp;nbsp;로드하고&amp;nbsp;실행. &lt;br /&gt;&lt;br /&gt;런타임&amp;nbsp;환경&amp;nbsp;제공.&amp;nbsp;-&amp;gt;&amp;nbsp;네트워크&amp;nbsp;처리,&amp;nbsp;파일&amp;nbsp;시스템&amp;nbsp;접근,&amp;nbsp;스레드&amp;nbsp;관리,&amp;nbsp;&amp;nbsp;/&amp;nbsp;앱이&amp;nbsp;운영체제의&amp;nbsp;의존성을&amp;nbsp;최소화하고&amp;nbsp;동일하게&amp;nbsp;동작하도록&amp;nbsp;도움. &lt;br /&gt;&lt;br /&gt;보안&amp;nbsp;역할&amp;nbsp;-&amp;gt;&amp;nbsp;악의적&amp;nbsp;코드&amp;nbsp;실행&amp;nbsp;방지/&amp;nbsp;불법&amp;nbsp;메모리&amp;nbsp;접근&amp;nbsp;차단&amp;nbsp;/&amp;nbsp;시스템&amp;nbsp;자원&amp;nbsp;오남용&amp;nbsp;방지 &lt;br /&gt;&lt;br /&gt;성능관리&amp;nbsp;및&amp;nbsp;모니터링&amp;nbsp;-&amp;gt;&amp;nbsp;JMX&amp;nbsp;/&amp;nbsp;JFR&amp;nbsp;/&amp;nbsp;VisualVM&amp;nbsp;/&amp;nbsp;GC로그&amp;nbsp;분석&amp;nbsp;제공 &lt;br /&gt;&lt;br /&gt;면접에서&amp;nbsp;질문한&amp;nbsp;의도&amp;nbsp;:&amp;nbsp; &lt;br /&gt;&lt;br /&gt;자바의&amp;nbsp;기초&amp;nbsp;기술&amp;nbsp;이해도 &lt;br /&gt;자바&amp;nbsp;개발의&amp;nbsp;기초이기에&amp;nbsp;언어와&amp;nbsp;플랫폼의&amp;nbsp;근본원리를&amp;nbsp;제대로&amp;nbsp;이해하고&amp;nbsp;있는지&amp;nbsp;확인. &lt;br /&gt;&lt;br /&gt;시스템&amp;nbsp;구조적&amp;nbsp;사고&amp;nbsp;능력&amp;nbsp;-&amp;gt;&amp;nbsp;실행&amp;nbsp;구조&amp;nbsp;전체와&amp;nbsp;연결되어&amp;nbsp;있으니&amp;nbsp;jvm을&amp;nbsp;설명하는&amp;nbsp;과정에서&amp;nbsp;지원자가&amp;nbsp;시스템을&amp;nbsp;전체적으로&amp;nbsp;바라보는&amp;nbsp;능력을&amp;nbsp;평가가능, &lt;br /&gt;&lt;br /&gt;실무경험&amp;nbsp;기반&amp;nbsp;설명&amp;nbsp;능력&amp;nbsp;-&amp;gt;&amp;nbsp;GC튜닝&amp;nbsp;경험&amp;nbsp;/&amp;nbsp;JVM&amp;nbsp;옵션&amp;nbsp;사용&amp;nbsp;경험&amp;nbsp;/장애&amp;nbsp;대응에서&amp;nbsp;JVM&amp;nbsp;로그&amp;nbsp;분석&amp;nbsp;경험/&amp;nbsp;메모리&amp;nbsp;누수&amp;nbsp;해결&amp;nbsp;사례 &lt;br /&gt;&lt;br /&gt;자바&amp;nbsp;언어에&amp;nbsp;대한&amp;nbsp;이해도.&amp;nbsp;-&amp;gt;&amp;nbsp;왜&amp;nbsp;자바가&amp;nbsp;플랫폼&amp;nbsp;독립적인지.&amp;nbsp;/&amp;nbsp;왜&amp;nbsp;JVM&amp;nbsp;환경에서&amp;nbsp;메모리&amp;nbsp;누수가&amp;nbsp;발생할수&amp;nbsp;있는지/&amp;nbsp;왜&amp;nbsp;GC가&amp;nbsp;필요한지. &lt;br /&gt;&lt;br /&gt;메모리&amp;nbsp;누수가&amp;nbsp;발생하는&amp;nbsp;이유&amp;nbsp; &lt;br /&gt;1.객체가&amp;nbsp;아직&amp;nbsp;참조되기&amp;nbsp;때문. &lt;br /&gt;-&amp;gt;&amp;nbsp;컬렉션(List,&amp;nbsp;Map)에&amp;nbsp;객체를&amp;nbsp;넣고&amp;nbsp;제거하지&amp;nbsp;않음&amp;nbsp; &lt;br /&gt;-&amp;gt;&amp;nbsp;사용이&amp;nbsp;끝난&amp;nbsp;객체를&amp;nbsp;정적(static)&amp;nbsp;변수로&amp;nbsp;계속&amp;nbsp;참조 &lt;br /&gt;-&amp;gt;&amp;nbsp;오래&amp;nbsp;남아있는&amp;nbsp;캐시에&amp;nbsp;객체를&amp;nbsp;계속&amp;nbsp;쌓아둠 &lt;br /&gt;2.&amp;nbsp;Listener,&amp;nbsp;Callback&amp;nbsp;등록후&amp;nbsp;제거하지&amp;nbsp;않은&amp;nbsp;경우 &lt;br /&gt;&lt;br /&gt;3.ThreadLocal&amp;nbsp;오용 &lt;br /&gt;-&amp;gt;&amp;nbsp;ThreadLocal&amp;nbsp;사용후&amp;nbsp;remove()하지&amp;nbsp;않은&amp;nbsp;경우 &lt;br /&gt;-&amp;gt;&amp;nbsp;스레드&amp;nbsp;풀에서&amp;nbsp;재사용되는&amp;nbsp;스레드에&amp;nbsp;ThreadLocal&amp;nbsp;값이&amp;nbsp;그대로&amp;nbsp;남는&amp;nbsp;경우 &lt;br /&gt;&lt;br /&gt;4.&amp;nbsp;닫지&amp;nbsp;않는&amp;nbsp;외부&amp;nbsp;자원(파일&amp;nbsp;,소켓) &lt;br /&gt;&lt;br /&gt;5.&amp;nbsp;classLoader&amp;nbsp;누수 &lt;br /&gt;-&amp;gt;&amp;nbsp;정적&amp;nbsp;필드에&amp;nbsp;큰&amp;nbsp;객체를&amp;nbsp;보관 &lt;br /&gt;-&amp;gt;&amp;nbsp;스레드가&amp;nbsp;종료되지&amp;nbsp;않아&amp;nbsp;클래스&amp;nbsp;로더를&amp;nbsp;붙잡고있는&amp;nbsp;경우 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;인터프리터와&amp;nbsp;네이티브&amp;nbsp;코드의&amp;nbsp;차이점&amp;nbsp; &lt;br /&gt;1.&amp;nbsp;인터프리터 &lt;br /&gt;-&amp;gt;&amp;nbsp;바이트&amp;nbsp;코드를&amp;nbsp;한줄씩&amp;nbsp;읽고&amp;nbsp;바로&amp;nbsp;실행. &lt;br /&gt;-&amp;gt;&amp;nbsp;실행속도는&amp;nbsp;다소&amp;nbsp;느림. &lt;br /&gt;-&amp;gt;&amp;nbsp;초기&amp;nbsp;시작&amp;nbsp;시간이&amp;nbsp;빠름&amp;nbsp; &lt;br /&gt;-&amp;gt;&amp;nbsp;즉시&amp;nbsp;실행이&amp;nbsp;필요하거나&amp;nbsp;코드가&amp;nbsp;자주&amp;nbsp;실행되지&amp;nbsp;않는&amp;nbsp;경우&amp;nbsp;유리. &lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;네이티브&amp;nbsp;코드 &lt;br /&gt;-&amp;gt;&amp;nbsp;JVM이&amp;nbsp;코드&amp;nbsp;실행&amp;nbsp;패턴을&amp;nbsp;분석하여&amp;nbsp;자주&amp;nbsp;실행되는&amp;nbsp;코드를&amp;nbsp;최적화된&amp;nbsp;기계어로&amp;nbsp;컴파일&amp;nbsp;후&amp;nbsp;캐싱 &lt;br /&gt;-&amp;gt;&amp;nbsp;이후&amp;nbsp;인터프리팅&amp;nbsp;없이&amp;nbsp;네이티브&amp;nbsp;코드로만&amp;nbsp;실행.&amp;nbsp;/&amp;nbsp;매우&amp;nbsp;빠른&amp;nbsp;속도 &lt;br /&gt;-&amp;gt;&amp;nbsp;장기&amp;nbsp;실행&amp;nbsp;서버에서&amp;nbsp;큰&amp;nbsp;이점 &lt;br /&gt;&lt;br /&gt;JVM&amp;nbsp;메모리&amp;nbsp;영역&amp;nbsp;구조가&amp;nbsp;왜&amp;nbsp;최적화&amp;nbsp;구조인가. &lt;br /&gt;1.&amp;nbsp;스택과&amp;nbsp;힙의&amp;nbsp;분리 &lt;br /&gt;-&amp;gt;&amp;nbsp;스택&amp;nbsp;:&amp;nbsp;메서드&amp;nbsp;실행&amp;nbsp;정보,&amp;nbsp;지역&amp;nbsp;변수가&amp;nbsp;짧은&amp;nbsp;생명&amp;nbsp;주기로&amp;nbsp;빠르게&amp;nbsp;생성.삭제 &lt;br /&gt;-&amp;gt;&amp;nbsp;LIFO구조라&amp;nbsp;매우&amp;nbsp;빠르게&amp;nbsp;관리가능. &lt;br /&gt;-&amp;gt;&amp;nbsp;힙:&amp;nbsp;객체가&amp;nbsp;오래&amp;nbsp;살아남고&amp;nbsp;구조가&amp;nbsp;복잡함. &lt;br /&gt;-&amp;gt;&amp;nbsp;GC가&amp;nbsp;필요. &lt;br /&gt;&lt;br /&gt;=&amp;gt;&amp;nbsp;생존&amp;nbsp;주기가&amp;nbsp;다른&amp;nbsp;데이터를&amp;nbsp;분리하여&amp;nbsp;최적의&amp;nbsp;관리&amp;nbsp;방식. &lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;Young&amp;nbsp;/&amp;nbsp;old&amp;nbsp;Generation &lt;br /&gt;-&amp;gt;&amp;nbsp;young&amp;nbsp;:&amp;nbsp;짧게&amp;nbsp;살아가는&amp;nbsp;객체&amp;nbsp;/&amp;nbsp;빠른&amp;nbsp;Minor&amp;nbsp;GC &lt;br /&gt;-&amp;gt;&amp;nbsp;old&amp;nbsp;:&amp;nbsp;오래&amp;nbsp;살아남는&amp;nbsp;객체&amp;nbsp;/&amp;nbsp;상대적으로&amp;nbsp;느린&amp;nbsp;Major&amp;nbsp;GC&amp;nbsp;주기적&amp;nbsp;실행 &lt;br /&gt;&lt;br /&gt;3.&amp;nbsp;Metaspace&amp;nbsp;/&amp;nbsp;Method &amp;nbsp;Area&amp;nbsp;분리 &lt;br /&gt;Metaspace는&amp;nbsp;JVM의&amp;nbsp;Method&amp;nbsp;Area를&amp;nbsp;대체하는&amp;nbsp;영역이며,&amp;nbsp;클래스&amp;nbsp;메타데이터를&amp;nbsp;저장한다. &lt;br /&gt;(Java&amp;nbsp;8&amp;nbsp;이전:&amp;nbsp;PermGen,&amp;nbsp;이후:&amp;nbsp;Metaspace) &lt;br /&gt;&lt;br /&gt;JVM에서&amp;nbsp;보안&amp;nbsp;역할은&amp;nbsp;어떻게&amp;nbsp;동작하는가. &lt;br /&gt;1.&amp;nbsp;바이트&amp;nbsp;코드&amp;nbsp;검증 &lt;br /&gt;-&amp;gt;&amp;nbsp;클래스&amp;nbsp;로딩시&amp;nbsp;바이트&amp;nbsp;코드를&amp;nbsp;검증하며 &lt;br /&gt;-&amp;gt;&amp;nbsp;불법&amp;nbsp;메모리&amp;nbsp;접근 &lt;br /&gt;-&amp;gt;&amp;nbsp;스택&amp;nbsp;오버플로 &lt;br /&gt;-&amp;gt;&amp;nbsp;유효하지&amp;nbsp;않은&amp;nbsp;점프&amp;nbsp;등을&amp;nbsp;차단. &lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;Class&amp;nbsp;loader&amp;nbsp;보안 &lt;br /&gt;-&amp;gt;&amp;nbsp;상위&amp;nbsp;로더가&amp;nbsp;로드한&amp;nbsp;클래스는&amp;nbsp;하위로더가&amp;nbsp;재정의&amp;nbsp;불가 &lt;br /&gt;-&amp;gt;&amp;nbsp;악성코드가&amp;nbsp;중요&amp;nbsp;클래스&amp;nbsp;바꿔치기&amp;nbsp;방지. &lt;br /&gt;&lt;br /&gt;3.&amp;nbsp;Security&amp;nbsp;Manager &lt;br /&gt;-&amp;gt;&amp;nbsp;권한기반&amp;nbsp;제어로&amp;nbsp;파일&amp;nbsp;접근,&amp;nbsp;네트워크&amp;nbsp;접근을&amp;nbsp;제한&amp;nbsp;하는&amp;nbsp;역할. &lt;br /&gt;Java&amp;nbsp;17부터&amp;nbsp;deprecated &lt;br /&gt;&lt;br /&gt;4.&amp;nbsp;Sandbox&amp;nbsp;실행 &lt;br /&gt;-&amp;gt;&amp;nbsp;서명되지&amp;nbsp;않은&amp;nbsp;코드나&amp;nbsp;외부코드(Applet&amp;nbsp;시절)는&amp;nbsp;Sandbox에서&amp;nbsp;격리. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daily Dev Q&amp;amp;A</category>
      <author>awake123</author>
      <guid isPermaLink="true">https://awake123.tistory.com/60</guid>
      <comments>https://awake123.tistory.com/60#entry60comment</comments>
      <pubDate>Sun, 18 Jan 2026 23:34:32 +0900</pubDate>
    </item>
    <item>
      <title>application context</title>
      <link>https://awake123.tistory.com/59</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;application context&amp;rdquo;는 &lt;b&gt;스프링(Spring)에서 애플리케이션이 동작하는 데 필요한 모든 객체(Bean)와 설정을 담고 관리하는 컨테이너&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;188&quot; data-start=&quot;160&quot; data-ke-size=&quot;size23&quot;&gt;ApplicationContext의 역할&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;489&quot; data-start=&quot;189&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;292&quot; data-start=&quot;189&quot;&gt;&lt;b&gt;빈(Bean) 생성/관리&lt;/b&gt;: @Component, @Service, @Repository, @Configuration 등으로 등록된 객체를 만들고 주입해줌(DI)&lt;/li&gt;
&lt;li data-end=&quot;381&quot; data-start=&quot;293&quot;&gt;&lt;b&gt;설정 로딩&lt;/b&gt;: application.yml/properties, @Configuration 설정, 프로파일(dev, prod) 등 반영&lt;/li&gt;
&lt;li data-end=&quot;442&quot; data-start=&quot;382&quot;&gt;&lt;b&gt;라이프사이클 관리&lt;/b&gt;: 초기화/종료 콜백, 스코프(singleton, request 등) 관리&lt;/li&gt;
&lt;li data-end=&quot;489&quot; data-start=&quot;443&quot;&gt;&lt;b&gt;부가 기능 제공&lt;/b&gt;: 이벤트 발행/구독, 국제화(i18n), 리소스 로딩 등&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;BeanFactory와의 차이점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;548&quot; data-start=&quot;516&quot;&gt;BeanFactory는 &amp;ldquo;빈 관리&amp;rdquo;의 최소 기능만.&lt;/li&gt;
&lt;li data-end=&quot;629&quot; data-start=&quot;549&quot;&gt;ApplicationContext는 **BeanFactory + 각종 편의 기능(이벤트, i18n, 리소스 등)**을 포함한 &amp;ldquo;확장판&amp;rdquo;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;656&quot; data-start=&quot;631&quot; data-ke-size=&quot;size23&quot;&gt;웹에서 자주 나오는 &amp;ldquo;컨텍스트&amp;rdquo; 2가지&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;794&quot; data-start=&quot;657&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;709&quot; data-start=&quot;657&quot;&gt;&lt;b&gt;Root ApplicationContext&lt;/b&gt;: 서비스/레포지토리 등 &amp;ldquo;공통 빈&amp;rdquo; 중심&lt;/li&gt;
&lt;li data-end=&quot;794&quot; data-start=&quot;710&quot;&gt;&lt;b&gt;Servlet(Web) ApplicationContext&lt;/b&gt;: 컨트롤러/웹 관련 빈 중심 (DispatcherServlet이 갖는 컨텍스트)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-start=&quot;160&quot; data-end=&quot;188&quot; data-ke-size=&quot;size23&quot;&gt;ApplicationContext가 생성되는 시점&lt;br /&gt;&lt;br /&gt;&lt;/h3&gt;
&lt;h3 data-end=&quot;220&quot; data-start=&quot;211&quot; data-ke-size=&quot;size23&quot;&gt;실행 시작&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;378&quot; data-start=&quot;221&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;234&quot; data-start=&quot;221&quot;&gt;main()&lt;/li&gt;
&lt;li data-end=&quot;267&quot; data-start=&quot;235&quot;&gt;SpringApplication.run(&amp;hellip;) 호출&lt;/li&gt;
&lt;li data-end=&quot;378&quot; data-start=&quot;268&quot;&gt;스프링부트가 환경을 보고 &lt;b&gt;어떤 ApplicationContext를 쓸지 결정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;378&quot; data-start=&quot;321&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;378&quot; data-start=&quot;321&quot;&gt;웹 앱이면 보통 ServletWebServerApplicationContext (톰캣 내장 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-end=&quot;413&quot; data-start=&quot;380&quot; data-ke-size=&quot;size23&quot;&gt;핵심: refresh()에서 컨테이너가 &amp;ldquo;살아남&amp;rdquo;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;575&quot; data-start=&quot;414&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;575&quot; data-start=&quot;414&quot;&gt;ApplicationContext.refresh() 실행 (여기가 핵심 단계)
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;575&quot; data-start=&quot;466&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;508&quot; data-start=&quot;466&quot;&gt;설정/클래스 스캔 결과를 &amp;ldquo;빈 정의(BeanDefinition)&amp;rdquo;로 등록&lt;/li&gt;
&lt;li data-end=&quot;550&quot; data-start=&quot;512&quot;&gt;그 빈들을 실제 객체로 만들고(싱글톤 위주) 의존성 주입까지 완료&lt;/li&gt;
&lt;li data-end=&quot;575&quot; data-start=&quot;554&quot;&gt;각종 후처리기/프록시(AOP) 준비&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-end=&quot;590&quot; data-start=&quot;577&quot; data-ke-size=&quot;size23&quot;&gt;서버/서블릿 시작&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;678&quot; data-start=&quot;591&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;605&quot; data-start=&quot;591&quot;&gt;내장 톰캣/서버 시작&lt;/li&gt;
&lt;li data-end=&quot;637&quot; data-start=&quot;606&quot;&gt;DispatcherServlet 등록 및 초기화&lt;/li&gt;
&lt;li data-end=&quot;678&quot; data-start=&quot;638&quot;&gt;요청이 들어오면 DispatcherServlet이 컨트롤러 호출&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-end=&quot;855&quot; data-start=&quot;821&quot; data-ke-size=&quot;size26&quot;&gt;2) @Autowired가 왜 되는가 (DI 동작 원리)&lt;/h2&gt;
&lt;h3 data-end=&quot;863&quot; data-start=&quot;857&quot; data-ke-size=&quot;size23&quot;&gt;전제&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1001&quot; data-start=&quot;864&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;940&quot; data-start=&quot;864&quot;&gt;@Component / @Service / @Repository / @Configuration 같은 것들이 &lt;b&gt;스캔되거나&lt;/b&gt;,&lt;/li&gt;
&lt;li data-end=&quot;1001&quot; data-start=&quot;941&quot;&gt;@Bean 메서드로 등록되면&lt;br /&gt;&amp;rarr; ApplicationContext 안에 &lt;b&gt;빈으로 등록&lt;/b&gt;됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1019&quot; data-start=&quot;1003&quot; data-ke-size=&quot;size23&quot;&gt;주입이 일어나는 타이밍&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1089&quot; data-start=&quot;1020&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1048&quot; data-start=&quot;1020&quot;&gt;refresh() 과정에서 빈을 생성할 때,&lt;/li&gt;
&lt;li data-end=&quot;1089&quot; data-start=&quot;1049&quot;&gt;스프링이 &amp;ldquo;이 빈이 필요로 하는 의존성&amp;rdquo;을 확인하고 찾아서 넣습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1107&quot; data-start=&quot;1091&quot; data-ke-size=&quot;size23&quot;&gt;어떻게 찾나 (정확히)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1235&quot; data-start=&quot;1108&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1134&quot; data-start=&quot;1108&quot;&gt;기본은 &lt;b&gt;타입(Type) 기준&lt;/b&gt;으로 찾음&lt;/li&gt;
&lt;li data-end=&quot;1235&quot; data-start=&quot;1135&quot;&gt;후보가 여러 개면:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1235&quot; data-start=&quot;1150&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1166&quot; data-start=&quot;1150&quot;&gt;@Primary가 우선&lt;/li&gt;
&lt;li data-end=&quot;1197&quot; data-start=&quot;1169&quot;&gt;또는 @Qualifier(&quot;빈이름&quot;)로 지정&lt;/li&gt;
&lt;li data-end=&quot;1235&quot; data-start=&quot;1200&quot;&gt;또는 주입 지점을 이름/파라미터명 등으로 매칭(상황에 따라)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1379&quot; data-start=&quot;1340&quot; data-ke-size=&quot;size26&quot;&gt;3) @Transactional이 왜 되는가 (AOP + 프록시)&lt;/h2&gt;
&lt;p data-end=&quot;1388&quot; data-start=&quot;1381&quot; data-ke-size=&quot;size16&quot;&gt;핵심 한 줄:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1460&quot; data-start=&quot;1389&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1460&quot; data-start=&quot;1389&quot;&gt;@Transactional은 &amp;ldquo;그 메서드 호출을 가로채서&amp;rdquo; &lt;b&gt;트랜잭션 시작/커밋/롤백을 자동으로 해주는 AOP&lt;/b&gt;예요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1477&quot; data-start=&quot;1462&quot; data-ke-size=&quot;size23&quot;&gt;동작 방식 (프록시)&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1664&quot; data-start=&quot;1478&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1509&quot; data-start=&quot;1478&quot;&gt;ApplicationContext가 빈을 만들 때,&lt;/li&gt;
&lt;li data-end=&quot;1546&quot; data-start=&quot;1510&quot;&gt;&amp;ldquo;이 빈에 트랜잭션 어드바이스가 적용되어야 한다&amp;rdquo;를 감지하면&lt;/li&gt;
&lt;li data-end=&quot;1587&quot; data-start=&quot;1547&quot;&gt;&lt;b&gt;원본 객체 대신 프록시 객체(대리자)&lt;/b&gt; 를 빈으로 등록합니다.&lt;/li&gt;
&lt;li data-end=&quot;1664&quot; data-start=&quot;1588&quot;&gt;외부에서 그 빈의 메서드를 호출하면&lt;br /&gt;&amp;rarr; 프록시가 먼저 실행되어 트랜잭션을 처리한 뒤&lt;br /&gt;&amp;rarr; 실제 메서드를 호출합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1974&quot; data-start=&quot;1955&quot; data-ke-size=&quot;size23&quot;&gt;JPA/MyBatis와 연결&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2211&quot; data-start=&quot;1975&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2050&quot; data-start=&quot;1975&quot;&gt;@Transactional이 붙으면 스프링은 내부에서 PlatformTransactionManager를 통해 트랜잭션을 관리&lt;/li&gt;
&lt;li data-end=&quot;2134&quot; data-start=&quot;2051&quot;&gt;JPA면 보통 JpaTransactionManager, MyBatis/ JDBC면 DataSourceTransactionManager 계열&lt;/li&gt;
&lt;li data-end=&quot;2211&quot; data-start=&quot;2135&quot;&gt;같은 트랜잭션 안에서 JPA와 MyBatis를 섞으면 &lt;b&gt;같은 DataSource를 공유하는지&lt;/b&gt; 같은 것들이 중요 포인트가 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;288&quot; data-start=&quot;242&quot; data-ke-size=&quot;size26&quot;&gt;1) @Autowired가 나오는 이유: &lt;b&gt;컨테이너가 DI를 해주기 때문&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;514&quot; data-start=&quot;289&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;350&quot; data-start=&quot;289&quot;&gt;@Autowired는 &amp;ldquo;객체를 스프링이 만들어서 관리할 테니, 필요한 의존성을 찾아 넣어줘&amp;rdquo;라는 의미.&lt;/li&gt;
&lt;li data-end=&quot;392&quot; data-start=&quot;351&quot;&gt;&lt;b&gt;그 &amp;lsquo;찾아 넣는 주체&amp;rsquo;가 ApplicationContext&lt;/b&gt;예요.&lt;/li&gt;
&lt;li data-end=&quot;514&quot; data-start=&quot;393&quot;&gt;그래서 ApplicationContext를 설명하면 보통
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;514&quot; data-start=&quot;429&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;437&quot; data-start=&quot;429&quot;&gt;&amp;ldquo;빈 등록&amp;rdquo;&lt;/li&gt;
&lt;li data-end=&quot;448&quot; data-start=&quot;440&quot;&gt;&amp;ldquo;빈 생성&amp;rdquo;&lt;/li&gt;
&lt;li data-end=&quot;514&quot; data-start=&quot;451&quot;&gt;&amp;ldquo;의존성 주입(DI)&amp;rdquo;&lt;br /&gt;이 흐름이 나오고, DI의 대표 어노테이션이 @Autowired라 같이 등장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;526&quot; data-start=&quot;516&quot; data-ke-size=&quot;size16&quot;&gt;컨테이너 밖에서는?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;603&quot; data-start=&quot;527&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;603&quot; data-start=&quot;527&quot;&gt;내가 new Service()로 직접 만들면 스프링이 관여를 못 해서 &lt;b&gt;@Autowired는 주입이 안 됨&lt;/b&gt;(null/에러).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;666&quot; data-start=&quot;610&quot; data-ke-size=&quot;size26&quot;&gt;2) @Transactional이 나오는 이유: &lt;b&gt;컨테이너가 AOP 프록시를 붙여주기 때문&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;837&quot; data-start=&quot;667&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;750&quot; data-start=&quot;667&quot;&gt;@Transactional은 메서드 호출을 가로채서
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;750&quot; data-start=&quot;702&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;750&quot; data-start=&quot;702&quot;&gt;트랜잭션 시작 &amp;rarr; 실제 메서드 실행 &amp;rarr; 커밋/롤백&lt;br /&gt;을 자동으로 해주는 기능인데,&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;781&quot; data-start=&quot;751&quot;&gt;이 &amp;ldquo;가로채기&amp;rdquo;가 &lt;b&gt;AOP 프록시&lt;/b&gt;로 구현되고,&lt;/li&gt;
&lt;li data-end=&quot;837&quot; data-start=&quot;782&quot;&gt;그 프록시를 붙여서 빈으로 등록해주는 게 &lt;b&gt;ApplicationContext&lt;/b&gt;의 역할이에요.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;849&quot; data-start=&quot;839&quot; data-ke-size=&quot;size16&quot;&gt;컨테이너 밖에서는?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;915&quot; data-start=&quot;850&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;915&quot; data-start=&quot;850&quot;&gt;new로 만든 객체는 프록시가 붙지 않아서 &lt;b&gt;@Transactional이 동작하지 않음&lt;/b&gt;(그냥 주석처럼 됨).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ApplicationContext를 사용하지 않는다면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;155&quot; data-start=&quot;127&quot; data-ke-size=&quot;size23&quot;&gt;1) DI(@Autowired) 동작 안 함&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;250&quot; data-start=&quot;156&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;190&quot; data-start=&quot;156&quot;&gt;내가 new로 직접 객체 만들면 스프링이 관여 못 해서&lt;/li&gt;
&lt;li data-end=&quot;250&quot; data-start=&quot;191&quot;&gt;@Autowired 주입이 안 되거나(필드 주입이면 null), 생성자에 직접 다 넣어줘야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1768718492981&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;OrderService s = new OrderService(); // new로 만들면
// s 안의 @Autowired repo는 주입 안 됨&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-end=&quot;397&quot; data-start=&quot;351&quot; data-ke-size=&quot;size23&quot;&gt;2) AOP 기반 기능이 거의 다 안 됨 (@Transactional 포함)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;549&quot; data-start=&quot;398&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;496&quot; data-start=&quot;398&quot;&gt;@Transactional, @Async, @Cacheable, @Retryable 같은 건&lt;br /&gt;&lt;b&gt;컨테이너가 프록시를 만들어 줄 때만&lt;/b&gt; 제대로 동작합니다.&lt;/li&gt;
&lt;li data-end=&quot;549&quot; data-start=&quot;497&quot;&gt;컨테이너 없이 new로 만들면 프록시가 없어서 &amp;ldquo;어노테이션이 있어도 효과 없음&amp;rdquo;이 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;571&quot; data-start=&quot;551&quot; data-ke-size=&quot;size23&quot;&gt;3) 설정/환경 기능도 못 씀&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;694&quot; data-start=&quot;572&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;649&quot; data-start=&quot;572&quot;&gt;@Value, @ConfigurationProperties, 프로파일(dev/prod), 자동 설정(autoconfig) 등&lt;/li&gt;
&lt;li data-end=&quot;694&quot; data-start=&quot;650&quot;&gt;전부 컨테이너가 설정을 읽고 빈에 반영해주는 구조라 사용이 매우 제한됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;727&quot; data-start=&quot;696&quot; data-ke-size=&quot;size23&quot;&gt;4) 웹(MVC) 자체가 사실상 불가능/매우 불편&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;846&quot; data-start=&quot;728&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;787&quot; data-start=&quot;728&quot;&gt;스프링 MVC는 DispatcherServlet이 컨트롤러 빈을 컨테이너에서 찾아 호출하는 구조라,&lt;/li&gt;
&lt;li data-end=&quot;846&quot; data-start=&quot;788&quot;&gt;컨테이너가 없으면 스프링 MVC를 쓰는 의미가 크게 줄고, 사실상 &amp;ldquo;순수 서블릿/필터&amp;rdquo;로 돌아갑니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하지 못할 경우 대안&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1072&quot; data-start=&quot;898&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;915&quot; data-start=&quot;898&quot;&gt;객체 생성: 직접 new&lt;/li&gt;
&lt;li data-end=&quot;943&quot; data-start=&quot;916&quot;&gt;의존성 연결: 생성자에 직접 넣기(수동 DI)&lt;/li&gt;
&lt;li data-end=&quot;988&quot; data-start=&quot;944&quot;&gt;트랜잭션: 직접 Connection 열고 commit/rollback&lt;/li&gt;
&lt;li data-end=&quot;1020&quot; data-start=&quot;989&quot;&gt;설정 값 로딩: 직접 properties 읽어서 전달&lt;/li&gt;
&lt;li data-end=&quot;1072&quot; data-start=&quot;1021&quot;&gt;공통 관심사(로깅/보안/검증): 직접 코드로 감싸기(데코레이터/프록시 패턴을 수동 구현)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1132&quot; data-start=&quot;1074&quot; data-ke-size=&quot;size16&quot;&gt;즉, 가능은 하지만 &lt;b&gt;스프링이 제공하는 생산성이 거의 사라지고 코드량/실수/중복이 크게 늘어납니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1132&quot; data-start=&quot;1074&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1132&quot; data-start=&quot;1074&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;현실적으로 안쓰는 경우는?&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1132&quot; data-start=&quot;1074&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1220&quot; data-start=&quot;1180&quot;&gt;아주 작은 유틸/배치/CLI에서 스프링 없이 자바로만 가볍게 만들 때&lt;/li&gt;
&lt;li data-end=&quot;1280&quot; data-start=&quot;1221&quot;&gt;스프링을 쓰더라도, 일부 아주 단순한 모듈은 순수 자바로 테스트할 때(단위 테스트에서 목 객체로 대체)&lt;/li&gt;
&lt;li data-end=&quot;1331&quot; data-start=&quot;1281&quot;&gt;반대로 &amp;ldquo;스프링부트 웹 서비스&amp;rdquo;라면 컨테이너 없이 운영하는 건 보통 선택지가 아닙니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꼬리 질문&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;131&quot; data-start=&quot;69&quot; data-ke-size=&quot;size23&quot;&gt;Q1. refresh()에서 &amp;ldquo;프록시(AOP)&amp;rdquo;가 준비된다고 했는데, 누가/어떻게 프록시를 붙이나요?&lt;/h3&gt;
&lt;p data-end=&quot;303&quot; data-start=&quot;132&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; 컨테이너가 빈을 생성한 뒤 &lt;b&gt;BeanPostProcessor&lt;/b&gt;(대표적으로 AOP 관련 후처리기)가 빈을 검사해서, @Transactional 같은 어드바이스 적용 대상이면 &lt;b&gt;원본 빈 대신 프록시 빈&lt;/b&gt;을 등록합니다. 그래서 외부 호출이 프록시를 먼저 타면서 트랜잭션이 시작/종료됩니다.&lt;/p&gt;
&lt;h3 data-end=&quot;363&quot; data-start=&quot;305&quot; data-ke-size=&quot;size23&quot;&gt;Q2. @Autowired는 &amp;ldquo;타입 기준&amp;rdquo;이라 했는데, 같은 타입 빈이 2개면 어떻게 되나요?&lt;/h3&gt;
&lt;p data-end=&quot;428&quot; data-start=&quot;364&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; 기본은 &lt;b&gt;NoUniqueBeanDefinitionException&lt;/b&gt;이 납니다. 해결은 보통 3가지:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;516&quot; data-start=&quot;429&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;450&quot; data-start=&quot;429&quot;&gt;@Primary로 기본 빈 지정&lt;/li&gt;
&lt;li data-end=&quot;476&quot; data-start=&quot;451&quot;&gt;@Qualifier(&quot;빈이름&quot;)로 명시&lt;/li&gt;
&lt;li data-end=&quot;516&quot; data-start=&quot;477&quot;&gt;주입 지점의 이름/파라미터명과 빈 이름을 맞춰서 해결(상황에 따라)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;571&quot; data-start=&quot;518&quot; data-ke-size=&quot;size23&quot;&gt;Q3. JPA와 MyBatis를 같은 트랜잭션에서 섞을 수 있다고 했는데 조건이 뭐예요?&lt;/h3&gt;
&lt;p data-end=&quot;795&quot; data-start=&quot;572&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; 핵심 조건은 &lt;b&gt;같은 DataSource(같은 트랜잭션 매니저 관할)&lt;/b&gt; 를 공유해야 합니다. @Transactional이 같은 트랜잭션 경계에서 동작하려면 JPA(EntityManager)와 MyBatis(SqlSession)가 결국 같은 커넥션/트랜잭션에 묶여야 합니다. 설정이 분리되어 있으면 &amp;ldquo;같은 @Transactional인데도&amp;rdquo; 실제론 서로 다른 트랜잭션이 될 수 있어요.&lt;/p&gt;
&lt;p data-end=&quot;795&quot; data-start=&quot;572&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;887&quot; data-start=&quot;835&quot; data-ke-size=&quot;size23&quot;&gt;Q4. ApplicationContext는 인터페이스인가요? 구현체는 뭐가 있어요?&lt;/h3&gt;
&lt;p data-end=&quot;943&quot; data-start=&quot;888&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; ApplicationContext는 &lt;b&gt;인터페이스&lt;/b&gt;이고, 상황별 구현체가 있어요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1104&quot; data-start=&quot;944&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;995&quot; data-start=&quot;944&quot;&gt;일반 애플리케이션: AnnotationConfigApplicationContext 등&lt;/li&gt;
&lt;li data-end=&quot;1104&quot; data-start=&quot;996&quot;&gt;스프링부트 웹: 보통 ServletWebServerApplicationContext&lt;br /&gt;웹/환경에 맞게 컨테이너 구현이 달라지지만 &amp;ldquo;빈 관리/DI/AOP 적용&amp;rdquo; 같은 핵심 역할은 동일합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1160&quot; data-start=&quot;1106&quot; data-ke-size=&quot;size23&quot;&gt;Q5. 빈 스코프가 singleton, request 등이라고 했는데 실제 차이는?&lt;/h3&gt;
&lt;p data-end=&quot;1167&quot; data-start=&quot;1161&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1359&quot; data-start=&quot;1168&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1201&quot; data-start=&quot;1168&quot;&gt;singleton: 컨테이너당 1개 인스턴스(기본값)&lt;/li&gt;
&lt;li data-end=&quot;1263&quot; data-start=&quot;1202&quot;&gt;prototype: 요청할 때마다 새로 생성(컨테이너가 &amp;ldquo;생성까지만&amp;rdquo; 관리, 이후 생명주기 관여 적음)&lt;/li&gt;
&lt;li data-end=&quot;1359&quot; data-start=&quot;1264&quot;&gt;request/session: 웹 요청/세션 단위로 1개(웹 컨텍스트에서 의미가 큼)&lt;br /&gt;스코프가 다르면 메모리/동시성/상태관리 방식이 달라져서 설계에 영향이 큽니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1404&quot; data-start=&quot;1361&quot; data-ke-size=&quot;size23&quot;&gt;Q6. 빈 생명주기(초기화/종료 콜백)는 어떤 방식으로 걸 수 있나요?&lt;/h3&gt;
&lt;p data-end=&quot;1427&quot; data-start=&quot;1405&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; 대표적으로 3가지가 많아요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1610&quot; data-start=&quot;1428&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1461&quot; data-start=&quot;1428&quot;&gt;@PostConstruct, @PreDestroy&lt;/li&gt;
&lt;li data-end=&quot;1509&quot; data-start=&quot;1462&quot;&gt;InitializingBean, DisposableBean 인터페이스 구현&lt;/li&gt;
&lt;li data-end=&quot;1610&quot; data-start=&quot;1510&quot;&gt;@Bean(initMethod=..., destroyMethod=...)&lt;br /&gt;스프링부트에서는 보통 @PostConstruct/@PreDestroy가 가장 깔끔하게 쓰입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1693&quot; data-start=&quot;1650&quot; data-ke-size=&quot;size23&quot;&gt;Q7. 빈(Bean)과 그냥 객체(new로 만든 객체)는 뭐가 달라요?&lt;/h3&gt;
&lt;p data-end=&quot;1843&quot; data-start=&quot;1694&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; 빈은 &lt;b&gt;스프링 컨테이너가 생성/관리하는 객체&lt;/b&gt;라서 DI, AOP(@Transactional 등), 설정 주입(@Value), 라이프사이클 관리 같은 혜택을 받습니다. 반면 new로 만든 객체는 스프링이 모르는 객체라 이런 기능이 거의 적용되지 않아요.&lt;/p&gt;
&lt;h3 data-end=&quot;1887&quot; data-start=&quot;1845&quot; data-ke-size=&quot;size23&quot;&gt;Q8. 왜 굳이 컨테이너를 쓰나요? 그냥 new로 만들어도 되잖아요.&lt;/h3&gt;
&lt;p data-end=&quot;1914&quot; data-start=&quot;1888&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; new로도 가능하지만 규모가 커지면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2054&quot; data-start=&quot;1915&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1930&quot; data-start=&quot;1915&quot;&gt;의존성 연결이 복잡해지고&lt;/li&gt;
&lt;li data-end=&quot;1966&quot; data-start=&quot;1931&quot;&gt;공통 기능(트랜잭션/로깅/보안)을 일일이 반복 구현해야 하고&lt;/li&gt;
&lt;li data-end=&quot;2054&quot; data-start=&quot;1967&quot;&gt;테스트/교체가 어려워집니다.&lt;br /&gt;컨테이너를 쓰면 객체 생성/조립/공통 기능을 표준 방식으로 처리해서 &lt;b&gt;중복과 실수를 줄이고 유지보수성이 좋아집니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2116&quot; data-start=&quot;2056&quot; data-ke-size=&quot;size23&quot;&gt;Q9. ApplicationContext는 어디서 &amp;ldquo;가져와서&amp;rdquo; 쓰나요? 내가 직접 new 해서 써요?&lt;/h3&gt;
&lt;p data-end=&quot;2209&quot; data-start=&quot;2117&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; 보통 웹/부트 애플리케이션에서는 &lt;b&gt;직접 꺼내서 쓰는 일이 거의 없고&lt;/b&gt;, 스프링부트가 실행 중에 컨텍스트를 만들고 빈들을 주입해줘요. 정말 필요할 때만&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2389&quot; data-start=&quot;2210&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2246&quot; data-start=&quot;2210&quot;&gt;생성자 주입으로 ApplicationContext를 받거나&lt;/li&gt;
&lt;li data-end=&quot;2279&quot; data-start=&quot;2247&quot;&gt;ApplicationContextAware를 쓰거나&lt;/li&gt;
&lt;li data-end=&quot;2389&quot; data-start=&quot;2280&quot;&gt;테스트에서 @SpringBootTest로 컨텍스트를 띄우는 식으로 접근합니다.&lt;br /&gt;하지만 &amp;ldquo;컨테이너를 코드에서 여기저기 꺼내 쓰는 패턴&amp;rdquo;은 남용하면 의존성이 꼬여서 보통 권장되지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daily Dev Q&amp;amp;A</category>
      <author>awake123</author>
      <guid isPermaLink="true">https://awake123.tistory.com/59</guid>
      <comments>https://awake123.tistory.com/59#entry59comment</comments>
      <pubDate>Fri, 16 Jan 2026 19:51:37 +0900</pubDate>
    </item>
    <item>
      <title>WAS와 웹서버</title>
      <link>https://awake123.tistory.com/55</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1.WAS와 웹서버의 차이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;130&quot; data-start=&quot;111&quot; data-ke-size=&quot;size26&quot;&gt;웹서버 (Web Server)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;343&quot; data-start=&quot;131&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;205&quot; data-start=&quot;131&quot;&gt;역할: &lt;b&gt;HTTP 요청을 받아서 정적인 리소스&lt;/b&gt;를 응답
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;205&quot; data-start=&quot;168&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;205&quot; data-start=&quot;168&quot;&gt;예: HTML, CSS, JS, 이미지, 동영상, 다운로드 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;301&quot; data-start=&quot;206&quot;&gt;특징
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;301&quot; data-start=&quot;213&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;237&quot; data-start=&quot;213&quot;&gt;정적 파일 서빙에 최적화(빠르고 가벼움)&lt;/li&gt;
&lt;li data-end=&quot;301&quot; data-start=&quot;240&quot;&gt;리버스 프록시 기능(요청을 뒤의 서버로 전달), 로드밸런싱, SSL 종료(HTTPS 처리) 등을 자주 담당&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;343&quot; data-start=&quot;302&quot;&gt;예시
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;343&quot; data-start=&quot;309&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;343&quot; data-start=&quot;309&quot;&gt;Nginx, Apache HTTP Server, Caddy&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;376&quot; data-start=&quot;345&quot; data-ke-size=&quot;size26&quot;&gt;WAS (Web Application Server)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;739&quot; data-start=&quot;377&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;482&quot; data-start=&quot;377&quot;&gt;역할: &lt;b&gt;웹 애플리케이션을 실행&lt;/b&gt;해서 &lt;b&gt;동적인 응답&lt;/b&gt;을 생성
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;482&quot; data-start=&quot;419&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;482&quot; data-start=&quot;419&quot;&gt;예: 로그인/회원가입, 게시판 CRUD, 결제 처리, 권한 체크, DB 조회 결과로 HTML/JSON 생성 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;578&quot; data-start=&quot;483&quot;&gt;특징
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;578&quot; data-start=&quot;490&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;538&quot; data-start=&quot;490&quot;&gt;비즈니스 로직 실행 + DB 연동 + 트랜잭션 + 세션/인증 등 &amp;ldquo;앱&amp;rdquo; 기능의 중심&lt;/li&gt;
&lt;li data-end=&quot;578&quot; data-start=&quot;541&quot;&gt;보통 프레임워크(Spring, Django 등)가 여기서 돌아감&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;739&quot; data-start=&quot;579&quot;&gt;예시
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;739&quot; data-start=&quot;586&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;683&quot; data-start=&quot;586&quot;&gt;Java: Tomcat(서블릿 컨테이너), Jetty, Undertow / (더 전통적 의미의 WAS: WebLogic, WebSphere, JBoss/WildFly 등)&lt;/li&gt;
&lt;li data-end=&quot;739&quot; data-start=&quot;686&quot;&gt;Node.js(Express/Nest)도 &amp;ldquo;WAS 역할&amp;rdquo;을 하는 애플리케이션 서버로 보면 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;767&quot; data-start=&quot;741&quot; data-ke-size=&quot;size26&quot;&gt;* 둘을 같이 쓰는 이유&lt;/h2&gt;
&lt;p data-end=&quot;834&quot; data-start=&quot;781&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클라이언트 &amp;rarr; 웹서버(Nginx) &amp;rarr; WAS(Tomcat/Spring Boot) &amp;rarr; DB&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;850&quot; data-start=&quot;836&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;850&quot; data-start=&quot;836&quot; data-ke-size=&quot;size16&quot;&gt;웹 :&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;946&quot; data-start=&quot;851&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;869&quot; data-start=&quot;851&quot;&gt;정적 파일은 바로 처리(빠름)&lt;/li&gt;
&lt;li data-end=&quot;895&quot; data-start=&quot;870&quot;&gt;동적 요청은 WAS로 전달(리버스 프록시)&lt;/li&gt;
&lt;li data-end=&quot;946&quot; data-start=&quot;896&quot;&gt;HTTPS, 압축(gzip/br), 캐싱, 로드밸런싱, 보안 헤더 같은 공통 처리 담당&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;957&quot; data-start=&quot;948&quot; data-ke-size=&quot;size16&quot;&gt;WAS:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1006&quot; data-start=&quot;958&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;974&quot; data-start=&quot;958&quot;&gt;API/비즈니스 로직 처리&lt;/li&gt;
&lt;li data-end=&quot;1006&quot; data-start=&quot;975&quot;&gt;DB 접근, 인증/인가, 트랜잭션 등 핵심 기능 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹서버와 was를 사용해본 경험&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp; 웹서버&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 npm run dev로 실행한 웹 서버&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. was&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세미 프로젝트, 학업중 과제 등 다양한 스프링 부트 프로젝트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;톰캣을 활용한 웹 페이지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*&amp;nbsp; 단 톰캣은 전통적인 was라기보다는 서블릿 컨테이너이며 전통적인 was는 WebLogic/WebSphere/WildFly&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. was와 웨서버가 제공하는 최적화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;141&quot; data-start=&quot;102&quot; data-ke-size=&quot;size26&quot;&gt;웹서버가 제공하는 최적화 (Nginx/Apache/Caddy 등)&lt;/h2&gt;
&lt;p data-end=&quot;186&quot; data-start=&quot;142&quot; data-ke-size=&quot;size16&quot;&gt;정리하면 &amp;ldquo;요청을 싸게 처리하고, 뒤쪽 서버(WAS)를 보호/가볍게&amp;rdquo; 해줍니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;800&quot; data-start=&quot;188&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;301&quot; data-start=&quot;188&quot;&gt;&lt;b&gt;정적 파일 고속 서빙&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;301&quot; data-start=&quot;208&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;248&quot; data-start=&quot;208&quot;&gt;OS 레벨 최적화(예: sendfile)로 파일을 효율적으로 전송&lt;/li&gt;
&lt;li data-end=&quot;301&quot; data-start=&quot;251&quot;&gt;정적 파일에 대한 캐시 헤더(ETag, Cache-Control) 설정으로 재요청 줄임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;389&quot; data-start=&quot;302&quot;&gt;&lt;b&gt;동시 접속 처리 효율&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;389&quot; data-start=&quot;322&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;360&quot; data-start=&quot;322&quot;&gt;이벤트 기반/비동기 처리로 커넥션을 가볍게 유지(특히 Nginx)&lt;/li&gt;
&lt;li data-end=&quot;389&quot; data-start=&quot;363&quot;&gt;Keep-Alive 관리, 대량 연결에 강함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;478&quot; data-start=&quot;390&quot;&gt;&lt;b&gt;압축/전송 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;478&quot; data-start=&quot;408&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;435&quot; data-start=&quot;408&quot;&gt;gzip / brotli 압축으로 트래픽 절감&lt;/li&gt;
&lt;li data-end=&quot;478&quot; data-start=&quot;438&quot;&gt;HTTP/2(그리고 환경에 따라 HTTP/3)로 다중화/헤더 압축 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;538&quot; data-start=&quot;479&quot;&gt;&lt;b&gt;TLS(HTTPS) 처리 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;538&quot; data-start=&quot;505&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;538&quot; data-start=&quot;505&quot;&gt;TLS 종료(암복호화)를 웹서버가 맡아 WAS 부담 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;626&quot; data-start=&quot;539&quot;&gt;&lt;b&gt;리버스 프록시 버퍼링&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;626&quot; data-start=&quot;559&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;626&quot; data-start=&quot;559&quot;&gt;느린 클라이언트에게 응답을 웹서버가 대신 천천히 보내고, WAS는 빨리 반환 가능(특히 업로드/다운로드에서 체감 큼)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;676&quot; data-start=&quot;627&quot;&gt;&lt;b&gt;로드밸런싱/헬스체크&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;676&quot; data-start=&quot;646&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;676&quot; data-start=&quot;646&quot;&gt;여러 WAS 인스턴스로 분산 + 장애 인스턴스 제외&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;747&quot; data-start=&quot;677&quot;&gt;&lt;b&gt;보안/안정성 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;747&quot; data-start=&quot;696&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;747&quot; data-start=&quot;696&quot;&gt;Rate limiting, IP 차단, WAF 연계, 요청 크기 제한 등으로 WAS 보호&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;800&quot; data-start=&quot;748&quot;&gt;&lt;b&gt;캐싱(선택)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;800&quot; data-start=&quot;763&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;800&quot; data-start=&quot;763&quot;&gt;자주 조회되는 응답을 웹서버/프록시 레벨에서 캐시(구성에 따라)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;844&quot; data-start=&quot;802&quot; data-ke-size=&quot;size26&quot;&gt;WAS가 제공하는 최적화 (Tomcat/Jetty + Spring 등)&lt;/h2&gt;
&lt;p data-end=&quot;886&quot; data-start=&quot;845&quot; data-ke-size=&quot;size16&quot;&gt;정리하면 &amp;ldquo;코드를 빠르고 안정적으로 실행하고, DB 작업을 효율화&amp;rdquo;합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1492&quot; data-start=&quot;888&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;976&quot; data-start=&quot;888&quot;&gt;&lt;b&gt;요청 처리 스레드/큐 관리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;976&quot; data-start=&quot;911&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;941&quot; data-start=&quot;911&quot;&gt;스레드 풀로 생성/파괴 비용을 줄이고 처리량 안정화&lt;/li&gt;
&lt;li data-end=&quot;976&quot; data-start=&quot;944&quot;&gt;백프레셔(큐/최대 동시 처리)로 과부하 시 무너짐 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1030&quot; data-start=&quot;977&quot;&gt;&lt;b&gt;세션/인증/인가 처리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1030&quot; data-start=&quot;997&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1030&quot; data-start=&quot;997&quot;&gt;세션(또는 토큰 검증) 처리 흐름 최적화(필터 체인 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1100&quot; data-start=&quot;1031&quot;&gt;&lt;b&gt;DB 최적화의 핵심: 커넥션 풀&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1100&quot; data-start=&quot;1057&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1100&quot; data-start=&quot;1057&quot;&gt;HikariCP 같은 커넥션 풀로 DB 연결 재사용(성능에 매우 큰 영향)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1228&quot; data-start=&quot;1101&quot;&gt;&lt;b&gt;쿼리 실행 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1228&quot; data-start=&quot;1119&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1161&quot; data-start=&quot;1119&quot;&gt;PreparedStatement 재사용, 배치 처리, 트랜잭션 경계 관리&lt;/li&gt;
&lt;li data-end=&quot;1228&quot; data-start=&quot;1164&quot;&gt;ORM(JPA) 사용 시 1차 캐시/지연 로딩 등으로 쿼리 패턴 최적화(잘 쓰면 이득, 잘못 쓰면 N+1 폭탄)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1292&quot; data-start=&quot;1229&quot;&gt;&lt;b&gt;애플리케이션 캐싱&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1292&quot; data-start=&quot;1247&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1292&quot; data-start=&quot;1247&quot;&gt;로컬 캐시(Caffeine), 분산 캐시(Redis) 등으로 DB hit 줄임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1357&quot; data-start=&quot;1293&quot;&gt;&lt;b&gt;직렬화/역직렬화 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1357&quot; data-start=&quot;1314&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1357&quot; data-start=&quot;1314&quot;&gt;JSON 파싱/생성(Jackson) 설정, 페이로드 크기 조절, 압축 연계&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1414&quot; data-start=&quot;1358&quot;&gt;&lt;b&gt;런타임 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1414&quot; data-start=&quot;1374&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1414&quot; data-start=&quot;1374&quot;&gt;JVM JIT 컴파일, GC 튜닝(필요 시), 클래스 로딩 최적화 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1492&quot; data-start=&quot;1415&quot;&gt;&lt;b&gt;비동기 처리/스트리밍(선택)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1492&quot; data-start=&quot;1439&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1492&quot; data-start=&quot;1439&quot;&gt;Async Servlet, WebFlux 같은 모델로 I/O 대기 효율 개선(케이스에 따라)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1503&quot; data-start=&quot;1494&quot; data-ke-size=&quot;size26&quot;&gt;요약.&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1609&quot; data-start=&quot;1504&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1553&quot; data-start=&quot;1504&quot;&gt;&lt;b&gt;웹서버 최적화 = &amp;ldquo;요청/정적/전송/보안/분산&amp;rdquo;을 싸게 처리해서 WAS를 보호&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1609&quot; data-start=&quot;1554&quot;&gt;&lt;b&gt;WAS 최적화 = &amp;ldquo;비즈니스 로직/DB/캐시/스레드&amp;rdquo;를 효율적으로 돌려 응답을 빨리 생성&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꼬리질문&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;134&quot; data-start=&quot;105&quot; data-ke-size=&quot;size26&quot;&gt;1) Tomcat은 웹서버인가요, WAS인가요?&lt;/h2&gt;
&lt;p data-end=&quot;279&quot; data-start=&quot;135&quot; data-ke-size=&quot;size16&quot;&gt;Tomcat은 &lt;b&gt;웹서버라기보단 서블릿 컨테이너&lt;/b&gt;이고, &lt;b&gt;자바 웹 애플리케이션을 실행&lt;/b&gt;해서 동적 응답을 만들기 때문에 실무에서는 &lt;b&gt;WAS처럼 사용&lt;/b&gt;합니다.&lt;br /&gt;다만 Nginx 같은 웹서버처럼 정적 파일/프록시/캐싱에 특화된 제품과는 역할이 다릅니다.&lt;/p&gt;
&lt;h2 data-end=&quot;340&quot; data-start=&quot;286&quot; data-ke-size=&quot;size26&quot;&gt;2) Spring Boot 내장 톰캣(JAR) vs 외부 Tomcat(WAR) 배포 차이는?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;549&quot; data-start=&quot;341&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;410&quot; data-start=&quot;341&quot;&gt;&lt;b&gt;JAR(내장 톰캣)&lt;/b&gt;: 애플리케이션 안에 서버가 포함돼서 java -jar로 실행 가능, 배포/실행이 단순함&lt;/li&gt;
&lt;li data-end=&quot;549&quot; data-start=&quot;411&quot;&gt;&lt;b&gt;WAR(외부 톰캣)&lt;/b&gt;: 톰캣이 별도 설치돼 있고 그 위에 WAR를 올리는 방식, 운영 표준이 그렇게 잡혀있는 조직에서 사용&lt;br /&gt;요즘은 보통 &lt;b&gt;Spring Boot JAR 방식이 더 흔하고&lt;/b&gt;, 레거시/기업 환경에선 WAR도 많이 봅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;595&quot; data-start=&quot;556&quot; data-ke-size=&quot;size26&quot;&gt;3) Nginx 리버스 프록시를 두면 요청 흐름은 어떻게 되나요?&lt;/h2&gt;
&lt;p data-end=&quot;625&quot; data-start=&quot;596&quot; data-ke-size=&quot;size16&quot;&gt;클라이언트 요청을 &lt;b&gt;Nginx가 먼저 받고&lt;/b&gt;,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;783&quot; data-start=&quot;626&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;671&quot; data-start=&quot;626&quot;&gt;/, /assets 같은 정적 경로는 &lt;b&gt;Nginx가 직접 서빙&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;783&quot; data-start=&quot;672&quot;&gt;/api 같은 동적 요청만 &lt;b&gt;WAS(Spring)로 프록시&lt;/b&gt;해서 전달합니다.&lt;br /&gt;이렇게 분리하면 WAS는 로직 처리에 집중하고, 정적/연결 처리는 웹서버가 맡아서 전체 성능이 안정적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;817&quot; data-start=&quot;790&quot; data-ke-size=&quot;size26&quot;&gt;4) 정적 파일 캐싱 전략은 어떻게 잡나요?&lt;/h2&gt;
&lt;p data-end=&quot;825&quot; data-start=&quot;818&quot; data-ke-size=&quot;size16&quot;&gt;기본 전략은:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1020&quot; data-start=&quot;826&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;916&quot; data-start=&quot;826&quot;&gt;파일명에 해시가 붙는 빌드 산출물(app.abc123.js)은 &lt;b&gt;Cache-Control: max-age 길게 + immutable&lt;/b&gt;로 강하게 캐싱&lt;/li&gt;
&lt;li data-end=&quot;1020&quot; data-start=&quot;917&quot;&gt;index.html처럼 엔트리 파일은 &lt;b&gt;짧게 캐싱하거나 no-cache&lt;/b&gt;로 두고, 새 배포 시 최신 번들을 가리키게 합니다.&lt;br /&gt;ETag를 쓰면 변경 시점 판단도 깔끔합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1063&quot; data-start=&quot;1027&quot; data-ke-size=&quot;size26&quot;&gt;5) HTTPS(SSL 종료)는 어디서 처리하는 게 좋나요?&lt;/h2&gt;
&lt;p data-end=&quot;1224&quot; data-start=&quot;1064&quot; data-ke-size=&quot;size16&quot;&gt;대부분은 &lt;b&gt;웹서버(Nginx)나 로드밸런서에서 SSL 종료&lt;/b&gt;를 합니다.&lt;br /&gt;이유는 TLS 처리를 앞단에서 통합하면 &lt;b&gt;WAS 부담이 줄고&lt;/b&gt;, 인증서 관리/갱신도 한 곳에서 관리돼 운영이 편합니다.&lt;br /&gt;내부망 구간은 HTTP로 두거나, 보안 요구가 높으면 내부도 TLS를 유지합니다.&lt;/p&gt;
&lt;h2 data-end=&quot;1256&quot; data-start=&quot;1231&quot; data-ke-size=&quot;size26&quot;&gt;6) WAS 성능 튜닝은 뭘 만져봤나요?&lt;/h2&gt;
&lt;p data-end=&quot;1295&quot; data-start=&quot;1257&quot; data-ke-size=&quot;size16&quot;&gt;핵심은 &amp;ldquo;WAS 자체&amp;rdquo;보다 &lt;b&gt;DB 접근 최적화&lt;/b&gt;가 체감이 큽니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1454&quot; data-start=&quot;1296&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1342&quot; data-start=&quot;1296&quot;&gt;&lt;b&gt;HikariCP 커넥션 풀&lt;/b&gt;(max pool size, timeout)&lt;/li&gt;
&lt;li data-end=&quot;1390&quot; data-start=&quot;1343&quot;&gt;&lt;b&gt;Tomcat 스레드 풀&lt;/b&gt;(max threads, accept queue)&lt;/li&gt;
&lt;li data-end=&quot;1454&quot; data-start=&quot;1391&quot;&gt;요청 타임아웃/리트라이 정책&lt;br /&gt;그리고 실제로는 &lt;b&gt;슬로우 쿼리/병목 로그&lt;/b&gt;를 보고 조정하는 게 정석입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1496&quot; data-start=&quot;1461&quot; data-ke-size=&quot;size26&quot;&gt;7) N+1 문제를 겪어봤다면 어떻게 발견하고 해결하나요?&lt;/h2&gt;
&lt;p data-end=&quot;1566&quot; data-start=&quot;1497&quot; data-ke-size=&quot;size16&quot;&gt;발견: 목록 조회에서 &lt;b&gt;쿼리가 1번이 아니라 엔티티 개수만큼 추가로 나가는지&lt;/b&gt; 로그로 확인합니다.&lt;br /&gt;해결: 상황에 따라&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1713&quot; data-start=&quot;1567&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1598&quot; data-start=&quot;1567&quot;&gt;&lt;b&gt;fetch join&lt;/b&gt;으로 한 번에 가져오거나&lt;/li&gt;
&lt;li data-end=&quot;1637&quot; data-start=&quot;1599&quot;&gt;&lt;b&gt;DTO projection&lt;/b&gt;으로 필요한 필드만 조회하거나&lt;/li&gt;
&lt;li data-end=&quot;1713&quot; data-start=&quot;1638&quot;&gt;@EntityGraph, batch size 설정으로 완화합니다.&lt;br /&gt;핵심은 &amp;ldquo;조회 목적에 맞게 쿼리 형태를 바꾸는 것&amp;rdquo;입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1751&quot; data-start=&quot;1720&quot; data-ke-size=&quot;size26&quot;&gt;8) 세션 기반 vs JWT 기반 인증의 장단점은?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1952&quot; data-start=&quot;1752&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1819&quot; data-start=&quot;1752&quot;&gt;&lt;b&gt;세션&lt;/b&gt;: 서버가 상태를 가짐(상태 기반). 구현이 직관적이지만 &lt;b&gt;수평 확장 시 세션 공유&lt;/b&gt;가 필요합니다.&lt;/li&gt;
&lt;li data-end=&quot;1952&quot; data-start=&quot;1820&quot;&gt;&lt;b&gt;JWT&lt;/b&gt;: 토큰에 인증 정보를 담아 &lt;b&gt;무상태(Stateless)&lt;/b&gt; 로 확장에 유리합니다. 대신 &lt;b&gt;토큰 탈취/폐기(로그아웃) 처리&lt;/b&gt;, 만료/리프레시 설계가 중요합니다.&lt;br /&gt;실무는 서비스 성격과 확장 요구에 따라 선택합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1992&quot; data-start=&quot;1959&quot; data-ke-size=&quot;size26&quot;&gt;9) WAS를 수평 확장하면 세션은 어떻게 처리하나요?&lt;/h2&gt;
&lt;p data-end=&quot;2007&quot; data-start=&quot;1993&quot; data-ke-size=&quot;size16&quot;&gt;대표 방법은 3가지입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;2201&quot; data-start=&quot;2008&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;2071&quot; data-start=&quot;2008&quot;&gt;&lt;b&gt;Sticky Session&lt;/b&gt;: 같은 사용자를 같은 서버로 붙임(간단하지만 장애/확장 유연성 떨어짐)&lt;/li&gt;
&lt;li data-end=&quot;2135&quot; data-start=&quot;2072&quot;&gt;&lt;b&gt;세션 스토리지 외부화&lt;/b&gt;: Redis 같은 곳에 세션 저장(Spring Session Redis 등)&lt;/li&gt;
&lt;li data-end=&quot;2201&quot; data-start=&quot;2136&quot;&gt;&lt;b&gt;JWT로 무상태화&lt;/b&gt;: 서버에 세션을 두지 않음&lt;br /&gt;확장성과 운영 안정성은 보통 2)나 3)이 더 좋습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-end=&quot;2245&quot; data-start=&quot;2208&quot; data-ke-size=&quot;size26&quot;&gt;10) 운영 배포 구조를 설계한다면 어떤 구성이 현실적인가요?&lt;/h2&gt;
&lt;p data-end=&quot;2253&quot; data-start=&quot;2246&quot; data-ke-size=&quot;size16&quot;&gt;대표적으로는:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2440&quot; data-start=&quot;2254&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2290&quot; data-start=&quot;2254&quot;&gt;&lt;b&gt;정적 프론트&lt;/b&gt;: CDN/정적 호스팅(또는 Nginx)&lt;/li&gt;
&lt;li data-end=&quot;2324&quot; data-start=&quot;2291&quot;&gt;&lt;b&gt;백엔드 API&lt;/b&gt;: Spring Boot(WAS)&lt;/li&gt;
&lt;li data-end=&quot;2370&quot; data-start=&quot;2325&quot;&gt;&lt;b&gt;앞단&lt;/b&gt;: Nginx 또는 로드밸런서(HTTPS 종료, 라우팅, 보안)&lt;/li&gt;
&lt;li data-end=&quot;2440&quot; data-start=&quot;2371&quot;&gt;&lt;b&gt;데이터&lt;/b&gt;: DB + 필요하면 Redis(캐시/세션)&lt;br /&gt;이 구조가 성능/보안/확장성 밸런스가 좋아서 가장 흔합니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Daily Dev Q&amp;amp;A</category>
      <author>awake123</author>
      <guid isPermaLink="true">https://awake123.tistory.com/55</guid>
      <comments>https://awake123.tistory.com/55#entry55comment</comments>
      <pubDate>Sun, 11 Jan 2026 13:38:28 +0900</pubDate>
    </item>
    <item>
      <title>Garbage Collection</title>
      <link>https://awake123.tistory.com/50</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. Garbage Collection 이란&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로그램이 사용하지 않게 된 메모리(객체)를 자동으로 찾아서 회수&lt;/b&gt;해 주는 메모리 관리 기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바처럼 객체를 new로 계속 만들면 힙(Heap)에 쌓이는데,&lt;br /&gt;그 객체를 더 이상 참조(사용)하지 않으면 메모리를 계속 차지합니다.&lt;br /&gt;GC는 이런 &lt;b&gt;&amp;ldquo;도달 불가능(unreachable)&amp;rdquo; 객체&lt;/b&gt;를 정리해서 힙 공간을 회수합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;634&quot; data-start=&quot;628&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;673&quot; data-start=&quot;635&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;662&quot; data-start=&quot;635&quot;&gt;메모리 해제 실수 방지(누수, 이중 해제 등)&lt;/li&gt;
&lt;li data-end=&quot;673&quot; data-start=&quot;663&quot;&gt;개발 생산성 &amp;uarr;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;681&quot; data-start=&quot;675&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;800&quot; data-start=&quot;682&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;742&quot; data-start=&quot;682&quot;&gt;GC가 돌 때 **일시 정지(STW: Stop-The-World)**가 발생할 수 있어 성능/지연에 영향&lt;/li&gt;
&lt;li data-end=&quot;800&quot; data-start=&quot;743&quot;&gt;&amp;ldquo;GC가 알아서 해주겠지&amp;rdquo;로 설계하면 참조를 오래 잡고 있어서 &lt;b&gt;메모리 사용량이 커질 수 있음&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 개념이 실무에서 없다면&lt;/p&gt;
&lt;h2 data-end=&quot;250&quot; data-start=&quot;216&quot; data-ke-size=&quot;size26&quot;&gt;1) 진짜로 GC가 없는 환경이라면 (수동 메모리 관리)&lt;/h2&gt;
&lt;p data-end=&quot;294&quot; data-start=&quot;251&quot; data-ke-size=&quot;size16&quot;&gt;GC가 없으면 &amp;ldquo;자동 청소&amp;rdquo;가 없어서, 아래를 개발자가 직접 책임져야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;522&quot; data-start=&quot;296&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;342&quot; data-start=&quot;296&quot;&gt;&lt;b&gt;메모리 누수(Leak)&lt;/b&gt;: 할당하고 해제를 안 하면 점점 RAM 먹고 죽음&lt;/li&gt;
&lt;li data-end=&quot;393&quot; data-start=&quot;343&quot;&gt;&lt;b&gt;이중 해제(Double free)&lt;/b&gt;: 같은 메모리를 두 번 해제 &amp;rarr; 크래시/취약점&lt;/li&gt;
&lt;li data-end=&quot;450&quot; data-start=&quot;394&quot;&gt;&lt;b&gt;댕글링 포인터(Dangling pointer)&lt;/b&gt;: 해제한 메모리를 다시 사용 &amp;rarr; 미정의 동작&lt;/li&gt;
&lt;li data-end=&quot;484&quot; data-start=&quot;451&quot;&gt;&lt;b&gt;Use-after-free&lt;/b&gt;: 보안 이슈로도 이어짐&lt;/li&gt;
&lt;li data-end=&quot;522&quot; data-start=&quot;485&quot;&gt;&lt;b&gt;소유권(누가 해제 책임?) 설계&lt;/b&gt;가 코드 전반의 핵심이 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;653&quot; data-start=&quot;608&quot; data-ke-size=&quot;size26&quot;&gt;2) GC가 있지만, &amp;ldquo;없다고 생각하고&amp;rdquo; 개발한다면 (자바/코틀린/Go 등)&lt;/h2&gt;
&lt;p data-end=&quot;698&quot; data-start=&quot;654&quot; data-ke-size=&quot;size16&quot;&gt;GC가 자동으로 해주긴 하지만, 개념을 모르면 실무에서 자주 터지는 게 있어요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1058&quot; data-start=&quot;700&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;796&quot; data-start=&quot;700&quot;&gt;&lt;b&gt;OOM(OutOfMemoryError)&lt;/b&gt;: 참조를 오래 잡아두면 GC가 못 치움&lt;br /&gt;예: static 컬렉션에 계속 쌓기, 캐시 무한 성장, 세션/리스너 미해제&lt;/li&gt;
&lt;li data-end=&quot;880&quot; data-start=&quot;797&quot;&gt;&lt;b&gt;지연/끊김(Stop-the-world)&lt;/b&gt;: 트래픽 많은 서버에서 GC가 길어지면 응답이 튐&lt;br /&gt;&amp;rarr; &amp;ldquo;가끔 2~5초 멈춤&amp;rdquo; 같은 장애 원인&lt;/li&gt;
&lt;li data-end=&quot;957&quot; data-start=&quot;881&quot;&gt;&lt;b&gt;성능 저하&lt;/b&gt;: 객체를 너무 많이/자주 만들면 GC가 바빠짐&lt;br /&gt;예: 문자열/컬렉션 남발, 불필요한 박싱, 큰 객체 반복 생성&lt;/li&gt;
&lt;li data-end=&quot;1058&quot; data-start=&quot;958&quot;&gt;&lt;b&gt;리소스 누수&lt;/b&gt;: 메모리 말고도&lt;br /&gt;파일/소켓/DB커넥션 같은 건 GC가 &amp;ldquo;즉시&amp;rdquo; 닫아주지 않음&lt;br /&gt;&amp;rarr; try-with-resources 같은 걸로 명시적으로 닫아야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1187&quot; data-start=&quot;1144&quot;&gt;&lt;b&gt;GC가 없으면&lt;/b&gt;: 메모리 생명주기 관리가 개발의 핵심 리스크가 됨&lt;/li&gt;
&lt;li data-end=&quot;1241&quot; data-start=&quot;1188&quot;&gt;&lt;b&gt;GC가 있는데 개념을 모르면&lt;/b&gt;: OOM/지연/성능/리소스 누수로 실무 장애가 나기 쉬움&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daily Dev Q&amp;amp;A</category>
      <author>awake123</author>
      <guid isPermaLink="true">https://awake123.tistory.com/50</guid>
      <comments>https://awake123.tistory.com/50#entry50comment</comments>
      <pubDate>Wed, 7 Jan 2026 16:46:36 +0900</pubDate>
    </item>
    <item>
      <title>자바 가상 머신</title>
      <link>https://awake123.tistory.com/47</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;자바&amp;nbsp;가상&amp;nbsp;머신 &lt;br /&gt;컴파일된&amp;nbsp;.class&amp;nbsp;파일(바이트코드)을&amp;nbsp;os관계&amp;nbsp;없이&amp;nbsp;실행시켜주는&amp;nbsp;도구 &lt;br /&gt;&lt;br /&gt;실무에서의&amp;nbsp;중요도 &lt;br /&gt;사용하지&amp;nbsp;못한다면&amp;nbsp;:&amp;nbsp; &lt;br /&gt;&lt;br /&gt;자바&amp;nbsp;바이트&amp;nbsp;코드&amp;nbsp;실행&amp;nbsp;불가.&amp;nbsp;-&amp;gt;&amp;nbsp;모든&amp;nbsp;자바기반&amp;nbsp;서비스&amp;nbsp;동작&amp;nbsp;불가. &lt;br /&gt;플랫폼&amp;nbsp;독립성&amp;nbsp;상실&amp;nbsp;-&amp;gt;&amp;nbsp;윈도우,&amp;nbsp;리눅스&amp;nbsp;,&amp;nbsp;맥에&amp;nbsp;맞게&amp;nbsp;매번&amp;nbsp;빌드해야함. &lt;br /&gt;가비지&amp;nbsp;컬렉션&amp;nbsp;기반&amp;nbsp;메모리&amp;nbsp;관리&amp;nbsp;기능&amp;nbsp;소실&amp;nbsp;-&amp;gt;&amp;nbsp;메모리&amp;nbsp;누수&amp;nbsp;위험&amp;nbsp;증가 &lt;br /&gt;자바&amp;nbsp;생태계&amp;nbsp;라이브러리,&amp;nbsp;프레임워크&amp;nbsp;사용&amp;nbsp;불가&amp;nbsp;-&amp;gt;&amp;nbsp;스프링기반&amp;nbsp;웹&amp;nbsp;서비스&amp;nbsp;구동&amp;nbsp;불가&amp;nbsp;/&amp;nbsp;마이크로서비스&amp;nbsp;기반&amp;nbsp;구조&amp;nbsp;대부분&amp;nbsp;장애. &lt;br /&gt;개발&amp;nbsp;운영&amp;nbsp;생산성&amp;nbsp;급락&amp;nbsp;-&amp;gt;&amp;nbsp;JIT&amp;nbsp;최적화&amp;nbsp;,&amp;nbsp;여러&amp;nbsp;성능의&amp;nbsp;튜닝&amp;nbsp;옵션,&amp;nbsp;풍부한&amp;nbsp;모니터링도구(JMX,Flight&amp;nbsp;Recorder)등&amp;nbsp;사라짐. &lt;br /&gt;&lt;br /&gt;금융권&amp;nbsp;백엔드&amp;nbsp;시스템,&amp;nbsp;공공기관&amp;nbsp;전산시스템,&amp;nbsp;대규모&amp;nbsp;웹서비스&amp;nbsp;등도&amp;nbsp;자바&amp;nbsp;기반이기에&amp;nbsp;사용불가. &lt;br /&gt;&lt;br /&gt;최적화/효율성&amp;nbsp;제공 &lt;br /&gt;최적화&amp;nbsp;: &lt;br /&gt;&lt;br /&gt;JIT&amp;nbsp;컴파일&amp;nbsp;-&amp;gt;&amp;nbsp;jvm은&amp;nbsp;처음에는&amp;nbsp;바이트&amp;nbsp;코드를&amp;nbsp;인터프리터로&amp;nbsp;실행시키지만&amp;nbsp;자주&amp;nbsp;실행되는&amp;nbsp;코드를&amp;nbsp;감지하면&amp;nbsp;네이티브&amp;nbsp;코드로&amp;nbsp;컴파일하여&amp;nbsp;속도를&amp;nbsp;향상시킨다. &lt;br /&gt;hotspot기반&amp;nbsp;실행&amp;nbsp;최적화&amp;nbsp;-&amp;gt;&amp;nbsp;코드&amp;nbsp;실행&amp;nbsp;패턴을&amp;nbsp;관찰하여&amp;nbsp;가장&amp;nbsp;많이&amp;nbsp;사용되는&amp;nbsp;부분(hot&amp;nbsp;spot)을&amp;nbsp;자동&amp;nbsp;최적화.&amp;nbsp;/&amp;nbsp;개발자가&amp;nbsp;성능&amp;nbsp;튜닝을&amp;nbsp;하지&amp;nbsp;않아도&amp;nbsp;지속적으로&amp;nbsp;개선. &lt;br /&gt;가비지&amp;nbsp;컬렉션&amp;nbsp;기반&amp;nbsp;메모리&amp;nbsp;최적화&amp;nbsp;-&amp;gt;&amp;nbsp;필요없는&amp;nbsp;객체를&amp;nbsp;제거하여&amp;nbsp;메모리&amp;nbsp;누수방지,&amp;nbsp;서버&amp;nbsp;안정성&amp;nbsp;향상,&amp;nbsp;등의&amp;nbsp;효과를&amp;nbsp;제공. &lt;br /&gt;멀티스레드&amp;nbsp;최적화&amp;nbsp;-&amp;gt;&amp;nbsp;스레드&amp;nbsp;스케쥴링,&amp;nbsp;&amp;nbsp;락&amp;nbsp;최적화,&amp;nbsp;병렬&amp;nbsp;가비지&amp;nbsp;컬렉션&amp;nbsp;으로&amp;nbsp;멀티코어&amp;nbsp;자원&amp;nbsp;최대&amp;nbsp;활용. &lt;br /&gt;&lt;br /&gt;효율성&amp;nbsp;: &lt;br /&gt;플랫폼&amp;nbsp;독립성&amp;nbsp;-&amp;gt;&amp;nbsp;바이트&amp;nbsp;코드는&amp;nbsp;어느&amp;nbsp;os에서나&amp;nbsp;자바&amp;nbsp;가상머신으로&amp;nbsp;동일하게&amp;nbsp;실행.&amp;nbsp;/&amp;nbsp;운영&amp;nbsp;비용&amp;nbsp;절감 &lt;br /&gt;메모리&amp;nbsp;영역&amp;nbsp;최적화&amp;nbsp;구조&amp;nbsp;-&amp;gt;&amp;nbsp;Heap&amp;nbsp;객체&amp;nbsp;저장&amp;nbsp;/&amp;nbsp;Stack&amp;nbsp;메서드&amp;nbsp;호출,&amp;nbsp;지역변수&amp;nbsp;/&amp;nbsp;Method&amp;nbsp;Area&amp;nbsp;클래스&amp;nbsp;정보&amp;nbsp;/&amp;nbsp;Native&amp;nbsp;Method&amp;nbsp;Stack&amp;nbsp;JNI&amp;nbsp;호출 &lt;br /&gt;라이브러리/프레임워크&amp;nbsp;생태계&amp;nbsp;활용&amp;nbsp;-&amp;gt;&amp;nbsp;자바,&amp;nbsp;코틀린,&amp;nbsp;스칼라,&amp;nbsp;그루비등&amp;nbsp;언어모두&amp;nbsp;동작가능&amp;nbsp;/&amp;nbsp;Spring&amp;nbsp;,&amp;nbsp;Hibernate&amp;nbsp;,&amp;nbsp;Spark,&amp;nbsp;Netty등&amp;nbsp;강력한&amp;nbsp;프레임워크 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;자바&amp;nbsp;가상머신의&amp;nbsp;주요&amp;nbsp;역할&amp;nbsp;:&amp;nbsp; &lt;br /&gt;바이트&amp;nbsp;코드를&amp;nbsp;로드하고&amp;nbsp;실행. &lt;br /&gt;&lt;br /&gt;런타임&amp;nbsp;환경&amp;nbsp;제공.&amp;nbsp;-&amp;gt;&amp;nbsp;네트워크&amp;nbsp;처리,&amp;nbsp;파일&amp;nbsp;시스템&amp;nbsp;접근,&amp;nbsp;스레드&amp;nbsp;관리,&amp;nbsp;&amp;nbsp;/&amp;nbsp;앱이&amp;nbsp;운영체제의&amp;nbsp;의존성을&amp;nbsp;최소화하고&amp;nbsp;동일하게&amp;nbsp;동작하도록&amp;nbsp;도움. &lt;br /&gt;&lt;br /&gt;보안&amp;nbsp;역할&amp;nbsp;-&amp;gt;&amp;nbsp;악의적&amp;nbsp;코드&amp;nbsp;실행&amp;nbsp;방지/&amp;nbsp;불법&amp;nbsp;메모리&amp;nbsp;접근&amp;nbsp;차단&amp;nbsp;/&amp;nbsp;시스템&amp;nbsp;자원&amp;nbsp;오남용&amp;nbsp;방지 &lt;br /&gt;&lt;br /&gt;성능관리&amp;nbsp;및&amp;nbsp;모니터링&amp;nbsp;-&amp;gt;&amp;nbsp;JMX&amp;nbsp;/&amp;nbsp;JFR&amp;nbsp;/&amp;nbsp;VisualVM&amp;nbsp;/&amp;nbsp;GC로그&amp;nbsp;분석&amp;nbsp;제공 &lt;br /&gt;&lt;br /&gt;면접에서&amp;nbsp;질문한&amp;nbsp;의도&amp;nbsp;:&amp;nbsp; &lt;br /&gt;&lt;br /&gt;자바의&amp;nbsp;기초&amp;nbsp;기술&amp;nbsp;이해도 &lt;br /&gt;자바&amp;nbsp;개발의&amp;nbsp;기초이기에&amp;nbsp;언어와&amp;nbsp;플랫폼의&amp;nbsp;근본원리를&amp;nbsp;제대로&amp;nbsp;이해하고&amp;nbsp;있는지&amp;nbsp;확인. &lt;br /&gt;&lt;br /&gt;시스템&amp;nbsp;구조적&amp;nbsp;사고&amp;nbsp;능력&amp;nbsp;-&amp;gt;&amp;nbsp;실행&amp;nbsp;구조&amp;nbsp;전체와&amp;nbsp;연결되어&amp;nbsp;있으니&amp;nbsp;jvm을&amp;nbsp;설명하는&amp;nbsp;과정에서&amp;nbsp;지원자가&amp;nbsp;시스템을&amp;nbsp;전체적으로&amp;nbsp;바라보는&amp;nbsp;능력을&amp;nbsp;평가가능, &lt;br /&gt;&lt;br /&gt;실무경험&amp;nbsp;기반&amp;nbsp;설명&amp;nbsp;능력&amp;nbsp;-&amp;gt;&amp;nbsp;GC튜닝&amp;nbsp;경험&amp;nbsp;/&amp;nbsp;JVM&amp;nbsp;옵션&amp;nbsp;사용&amp;nbsp;경험&amp;nbsp;/장애&amp;nbsp;대응에서&amp;nbsp;JVM&amp;nbsp;로그&amp;nbsp;분석&amp;nbsp;경험/&amp;nbsp;메모리&amp;nbsp;누수&amp;nbsp;해결&amp;nbsp;사례 &lt;br /&gt;&lt;br /&gt;자바&amp;nbsp;언어에&amp;nbsp;대한&amp;nbsp;이해도.&amp;nbsp;-&amp;gt;&amp;nbsp;왜&amp;nbsp;자바가&amp;nbsp;플랫폼&amp;nbsp;독립적인지.&amp;nbsp;/&amp;nbsp;왜&amp;nbsp;JVM&amp;nbsp;환경에서&amp;nbsp;메모리&amp;nbsp;누수가&amp;nbsp;발생할수&amp;nbsp;있는지/&amp;nbsp;왜&amp;nbsp;GC가&amp;nbsp;필요한지. &lt;br /&gt;&lt;br /&gt;메모리&amp;nbsp;누수가&amp;nbsp;발생하는&amp;nbsp;이유&amp;nbsp; &lt;br /&gt;1.객체가&amp;nbsp;아직&amp;nbsp;참조되기&amp;nbsp;때문. &lt;br /&gt;-&amp;gt;&amp;nbsp;컬렉션(List,&amp;nbsp;Map)에&amp;nbsp;객체를&amp;nbsp;넣고&amp;nbsp;제거하지&amp;nbsp;않음&amp;nbsp; &lt;br /&gt;-&amp;gt;&amp;nbsp;사용이&amp;nbsp;끝난&amp;nbsp;객체를&amp;nbsp;정적(static)&amp;nbsp;변수로&amp;nbsp;계속&amp;nbsp;참조 &lt;br /&gt;-&amp;gt;&amp;nbsp;오래&amp;nbsp;남아있는&amp;nbsp;캐시에&amp;nbsp;객체를&amp;nbsp;계속&amp;nbsp;쌓아둠 &lt;br /&gt;2.&amp;nbsp;Listener,&amp;nbsp;Callback&amp;nbsp;등록후&amp;nbsp;제거하지&amp;nbsp;않은&amp;nbsp;경우 &lt;br /&gt;&lt;br /&gt;3.ThreadLocal&amp;nbsp;오용 &lt;br /&gt;-&amp;gt;&amp;nbsp;ThreadLocal&amp;nbsp;사용후&amp;nbsp;remove()하지&amp;nbsp;않은&amp;nbsp;경우 &lt;br /&gt;-&amp;gt;&amp;nbsp;스레드&amp;nbsp;풀에서&amp;nbsp;재사용되는&amp;nbsp;스레드에&amp;nbsp;ThreadLocal&amp;nbsp;값이&amp;nbsp;그대로&amp;nbsp;남는&amp;nbsp;경우 &lt;br /&gt;&lt;br /&gt;4.&amp;nbsp;닫지&amp;nbsp;않는&amp;nbsp;외부&amp;nbsp;자원(파일&amp;nbsp;,소켓) &lt;br /&gt;&lt;br /&gt;5.&amp;nbsp;classLoader&amp;nbsp;누수 &lt;br /&gt;-&amp;gt;&amp;nbsp;정적&amp;nbsp;필드에&amp;nbsp;큰&amp;nbsp;객체를&amp;nbsp;보관 &lt;br /&gt;-&amp;gt;&amp;nbsp;스레드가&amp;nbsp;종료되지&amp;nbsp;않아&amp;nbsp;클래스&amp;nbsp;로더를&amp;nbsp;붙잡고있는&amp;nbsp;경우 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;인터프리터와&amp;nbsp;네이티브&amp;nbsp;코드의&amp;nbsp;차이점&amp;nbsp; &lt;br /&gt;1.&amp;nbsp;인터프리터 &lt;br /&gt;-&amp;gt;&amp;nbsp;바이트&amp;nbsp;코드를&amp;nbsp;한줄씩&amp;nbsp;읽고&amp;nbsp;바로&amp;nbsp;실행. &lt;br /&gt;-&amp;gt;&amp;nbsp;실행속도는&amp;nbsp;다소&amp;nbsp;느림. &lt;br /&gt;-&amp;gt;&amp;nbsp;초기&amp;nbsp;시작&amp;nbsp;시간이&amp;nbsp;빠름&amp;nbsp; &lt;br /&gt;-&amp;gt;&amp;nbsp;즉시&amp;nbsp;실행이&amp;nbsp;필요하거나&amp;nbsp;코드가&amp;nbsp;자주&amp;nbsp;실행되지&amp;nbsp;않는&amp;nbsp;경우&amp;nbsp;유리. &lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;네이티브&amp;nbsp;코드 &lt;br /&gt;-&amp;gt;&amp;nbsp;JVM이&amp;nbsp;코드&amp;nbsp;실행&amp;nbsp;패턴을&amp;nbsp;분석하여&amp;nbsp;자주&amp;nbsp;실행되는&amp;nbsp;코드를&amp;nbsp;최적화된&amp;nbsp;기계어로&amp;nbsp;컴파일&amp;nbsp;후&amp;nbsp;캐싱 &lt;br /&gt;-&amp;gt;&amp;nbsp;이후&amp;nbsp;인터프리팅&amp;nbsp;없이&amp;nbsp;네이티브&amp;nbsp;코드로만&amp;nbsp;실행.&amp;nbsp;/&amp;nbsp;매우&amp;nbsp;빠른&amp;nbsp;속도 &lt;br /&gt;-&amp;gt;&amp;nbsp;장기&amp;nbsp;실행&amp;nbsp;서버에서&amp;nbsp;큰&amp;nbsp;이점 &lt;br /&gt;&lt;br /&gt;JVM&amp;nbsp;메모리&amp;nbsp;영역&amp;nbsp;구조가&amp;nbsp;왜&amp;nbsp;최적화&amp;nbsp;구조인가. &lt;br /&gt;1.&amp;nbsp;스택과&amp;nbsp;힙의&amp;nbsp;분리 &lt;br /&gt;-&amp;gt;&amp;nbsp;스택&amp;nbsp;:&amp;nbsp;메서드&amp;nbsp;실행&amp;nbsp;정보,&amp;nbsp;지역&amp;nbsp;변수가&amp;nbsp;짧은&amp;nbsp;생명&amp;nbsp;주기로&amp;nbsp;빠르게&amp;nbsp;생성.삭제 &lt;br /&gt;-&amp;gt;&amp;nbsp;LIFO구조라&amp;nbsp;매우&amp;nbsp;빠르게&amp;nbsp;관리가능. &lt;br /&gt;-&amp;gt;&amp;nbsp;힙:&amp;nbsp;객체가&amp;nbsp;오래&amp;nbsp;살아남고&amp;nbsp;구조가&amp;nbsp;복잡함. &lt;br /&gt;-&amp;gt;&amp;nbsp;GC가&amp;nbsp;필요. &lt;br /&gt;&lt;br /&gt;=&amp;gt;&amp;nbsp;생존&amp;nbsp;주기가&amp;nbsp;다른&amp;nbsp;데이터를&amp;nbsp;분리하여&amp;nbsp;최적의&amp;nbsp;관리&amp;nbsp;방식. &lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;Young&amp;nbsp;/&amp;nbsp;old&amp;nbsp;Generation &lt;br /&gt;-&amp;gt;&amp;nbsp;young&amp;nbsp;:&amp;nbsp;짧게&amp;nbsp;살아가는&amp;nbsp;객체&amp;nbsp;/&amp;nbsp;빠른&amp;nbsp;Minor&amp;nbsp;GC &lt;br /&gt;-&amp;gt;&amp;nbsp;old&amp;nbsp;:&amp;nbsp;오래&amp;nbsp;살아남는&amp;nbsp;객체&amp;nbsp;/&amp;nbsp;상대적으로&amp;nbsp;느린&amp;nbsp;Major&amp;nbsp;GC&amp;nbsp;주기적&amp;nbsp;실행 &lt;br /&gt;&lt;br /&gt;3.&amp;nbsp;Metaspace&amp;nbsp;/&amp;nbsp;Method &amp;nbsp;Area&amp;nbsp;분리 &lt;br /&gt;Metaspace는&amp;nbsp;JVM의&amp;nbsp;Method&amp;nbsp;Area를&amp;nbsp;대체하는&amp;nbsp;영역이며,&amp;nbsp;클래스&amp;nbsp;메타데이터를&amp;nbsp;저장한다. &lt;br /&gt;(Java&amp;nbsp;8&amp;nbsp;이전:&amp;nbsp;PermGen,&amp;nbsp;이후:&amp;nbsp;Metaspace) &lt;br /&gt;&lt;br /&gt;JVM에서&amp;nbsp;보안&amp;nbsp;역할은&amp;nbsp;어떻게&amp;nbsp;동작하는가. &lt;br /&gt;1.&amp;nbsp;바이트&amp;nbsp;코드&amp;nbsp;검증 &lt;br /&gt;-&amp;gt;&amp;nbsp;클래스&amp;nbsp;로딩시&amp;nbsp;바이트&amp;nbsp;코드를&amp;nbsp;검증하며 &lt;br /&gt;-&amp;gt;&amp;nbsp;불법&amp;nbsp;메모리&amp;nbsp;접근 &lt;br /&gt;-&amp;gt;&amp;nbsp;스택&amp;nbsp;오버플로 &lt;br /&gt;-&amp;gt;&amp;nbsp;유효하지&amp;nbsp;않은&amp;nbsp;점프&amp;nbsp;등을&amp;nbsp;차단. &lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;Class&amp;nbsp;loader&amp;nbsp;보안 &lt;br /&gt;-&amp;gt;&amp;nbsp;상위&amp;nbsp;로더가&amp;nbsp;로드한&amp;nbsp;클래스는&amp;nbsp;하위로더가&amp;nbsp;재정의&amp;nbsp;불가 &lt;br /&gt;-&amp;gt;&amp;nbsp;악성코드가&amp;nbsp;중요&amp;nbsp;클래스&amp;nbsp;바꿔치기&amp;nbsp;방지. &lt;br /&gt;&lt;br /&gt;3.&amp;nbsp;Security&amp;nbsp;Manager &lt;br /&gt;-&amp;gt;&amp;nbsp;권한기반&amp;nbsp;제어로&amp;nbsp;파일&amp;nbsp;접근,&amp;nbsp;네트워크&amp;nbsp;접근을&amp;nbsp;제한&amp;nbsp;하는&amp;nbsp;역할. &lt;br /&gt;Java&amp;nbsp;17부터&amp;nbsp;deprecated &lt;br /&gt;&lt;br /&gt;4.&amp;nbsp;Sandbox&amp;nbsp;실행 &lt;br /&gt;-&amp;gt;&amp;nbsp;서명되지&amp;nbsp;않은&amp;nbsp;코드나&amp;nbsp;외부코드(Applet&amp;nbsp;시절)는&amp;nbsp;Sandbox에서&amp;nbsp;격리. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daily Dev Q&amp;amp;A</category>
      <author>awake123</author>
      <guid isPermaLink="true">https://awake123.tistory.com/47</guid>
      <comments>https://awake123.tistory.com/47#entry47comment</comments>
      <pubDate>Wed, 7 Jan 2026 08:47:38 +0900</pubDate>
    </item>
    <item>
      <title>spring의 예외처리방식</title>
      <link>https://awake123.tistory.com/44</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. spring의&amp;nbsp;예외처리방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 &amp;ldquo;예외 처리&amp;rdquo;는 크게 &lt;b&gt;(1) 예외가 전파/롤백되는 방식&lt;/b&gt;과 &lt;b&gt;(2) 컨트롤러 계층에서 HTTP 응답으로 바꾸는 방식&lt;/b&gt;으로 나눠서 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;133&quot; data-start=&quot;92&quot; data-ke-size=&quot;size26&quot;&gt;1) 예외 전파 &amp;amp; 트랜잭션 롤백 규칙 (@Transactional)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;265&quot; data-start=&quot;134&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;203&quot; data-start=&quot;134&quot;&gt;&lt;b&gt;Unchecked 예외(RuntimeException, Error)&lt;/b&gt;&lt;br /&gt;&amp;rarr; 기본적으로 트랜잭션 &lt;b&gt;롤백&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;265&quot; data-start=&quot;204&quot;&gt;&lt;b&gt;Checked 예외(Exception)&lt;/b&gt;&lt;br /&gt;&amp;rarr; 기본적으로 &lt;b&gt;롤백 안 함&lt;/b&gt; (커밋될 수 있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;277&quot; data-start=&quot;267&quot; data-ke-size=&quot;size16&quot;&gt;설정 가능:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;406&quot; data-start=&quot;278&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;339&quot; data-start=&quot;278&quot;&gt;@Transactional(rollbackFor = Exception.class) : 체크 예외도 롤백&lt;/li&gt;
&lt;li data-end=&quot;406&quot; data-start=&quot;340&quot;&gt;@Transactional(noRollbackFor = 특정예외.class) : 런타임 예외여도 롤백 제외 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의사항 :&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;492&quot; data-start=&quot;422&quot;&gt;&lt;b&gt;예외를 try-catch로 잡고 삼키면(throw 안 하면)&lt;/b&gt; 스프링은 &amp;ldquo;정상 처리&amp;rdquo;로 보고 &lt;b&gt;롤백 안 됩니다.&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;583&quot; data-start=&quot;493&quot;&gt;프록시 기반이라 &lt;b&gt;같은 클래스 내부 self-invocation&lt;/b&gt;(자기 메서드 호출)에서는 @Transactional이 적용 안 되는 함정이 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1-1. 기본 롤백 규칙: RuntimeException이면 롤백, Checked Exception이면 기본은 커밋&lt;/p&gt;
&lt;pre id=&quot;code_1767249757664&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@RequiredArgsConstructor
public class MemberService {
    private final MemberRepository repo;

    @Transactional
    public void runtimeExceptionRollback() {
        repo.save(new Member(&quot;a&quot;));
        throw new IllegalStateException(&quot;런타임 예외&quot;); // ✅ 기본 롤백
    }

    @Transactional
    public void checkedExceptionDefaultCommit() throws Exception {
        repo.save(new Member(&quot;b&quot;));
        throw new Exception(&quot;체크 예외&quot;); // ⚠️ 기본은 롤백 안 함(커밋될 수 있음)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1-2. 체크 예외도 롤백시키기: rollbackFor&lt;/p&gt;
&lt;pre id=&quot;code_1767249771162&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional(rollbackFor = Exception.class)
public void checkedExceptionRollback() throws Exception {
    repo.save(new Member(&quot;c&quot;));
    throw new Exception(&quot;체크 예외&quot;); // ✅ 롤백
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1-3. 예외를 잡아먹으면 롤백 안 됨&lt;/p&gt;
&lt;pre id=&quot;code_1767249783701&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
public void swallowedExceptionCommit() {
    repo.save(new Member(&quot;d&quot;));
    try {
        throw new IllegalStateException(&quot;문제&quot;);
    } catch (Exception e) {
        // 예외를 밖으로 던지지 않으면 스프링 입장에선 &quot;정상 종료&quot;로 보고 커밋될 수 있음
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1-4. 잡고 싶으면 롤백 마킹을 직접&lt;/p&gt;
&lt;pre id=&quot;code_1767249800154&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.transaction.interceptor.TransactionAspectSupport;

@Transactional
public void catchButRollback() {
    repo.save(new Member(&quot;e&quot;));
    try {
        throw new IllegalStateException(&quot;문제&quot;);
    } catch (Exception e) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // ✅ 강제 롤백
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1-5. @Transactional이 &amp;ldquo;안 먹는&amp;rdquo; 케이스(자기 자신 메서드 호출)&lt;/p&gt;
&lt;pre id=&quot;code_1767249812309&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class AService {

    @Transactional
    public void txMethod() {
        // 트랜잭션이 걸리길 기대
    }

    public void outer() {
        txMethod(); // ⚠️ 같은 클래스 내부 호출은 프록시를 안 거쳐서 트랜잭션이 적용 안 될 수 있음
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;628&quot; data-start=&quot;590&quot; data-ke-size=&quot;size26&quot;&gt;2) MVC/REST에서 예외를 HTTP 응답으로 변환하는 방식&lt;/h2&gt;
&lt;h3 data-end=&quot;665&quot; data-start=&quot;629&quot; data-ke-size=&quot;size23&quot;&gt;A. @ExceptionHandler (컨트롤러 내부)&lt;/h3&gt;
&lt;p data-end=&quot;690&quot; data-start=&quot;666&quot; data-ke-size=&quot;size16&quot;&gt;특정 컨트롤러에서만 예외 처리하고 싶을 때.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;768&quot; data-start=&quot;691&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;731&quot; data-start=&quot;691&quot;&gt;@ExceptionHandler(MyException.class)&lt;/li&gt;
&lt;li data-end=&quot;768&quot; data-start=&quot;732&quot;&gt;반환은 ResponseEntity 혹은 객체(&amp;rarr; JSON)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;830&quot; data-start=&quot;770&quot; data-ke-size=&quot;size23&quot;&gt;B. @ControllerAdvice / @RestControllerAdvice (전역 처리)&lt;/h3&gt;
&lt;p data-end=&quot;851&quot; data-start=&quot;831&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트 전체 컨트롤러에 공통 적용.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;889&quot; data-start=&quot;852&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;867&quot; data-start=&quot;852&quot;&gt;실무에서 제일 흔한 패턴&lt;/li&gt;
&lt;li data-end=&quot;889&quot; data-start=&quot;868&quot;&gt;예외별로 상태코드/메시지 통일 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;943&quot; data-start=&quot;891&quot; data-ke-size=&quot;size23&quot;&gt;C. ResponseStatusException / @ResponseStatus&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1071&quot; data-start=&quot;944&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1009&quot; data-start=&quot;944&quot;&gt;throw new ResponseStatusException(HttpStatus.NOT_FOUND, &quot;없음&quot;)&lt;/li&gt;
&lt;li data-end=&quot;1071&quot; data-start=&quot;1010&quot;&gt;또는 커스텀 예외 클래스에 @ResponseStatus(HttpStatus.BAD_REQUEST) 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-1. DTO 검증 실패 &amp;rarr; MethodArgumentNotValidException &amp;rarr; @RestControllerAdvice가 JSON 응답으로 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO&lt;/p&gt;
&lt;pre id=&quot;code_1767249836318&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SignupRequest {
    @NotBlank(message = &quot;사용자 ID는 필수입니다&quot;)
    @Size(min = 4, max = 20, message = &quot;사용자 ID는 4~20자&quot;)
    private String userId;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Controller&lt;/p&gt;
&lt;pre id=&quot;code_1767249846980&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@PostMapping(&quot;/api/members&quot;)
public ResponseEntity&amp;lt;Void&amp;gt; signup(@Valid @RequestBody SignupRequest req) {
    return ResponseEntity.ok().build();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 처리&lt;/p&gt;
&lt;pre id=&quot;code_1767249858594&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; handle(MethodArgumentNotValidException e) {
        Map&amp;lt;String, String&amp;gt; fieldErrors = new LinkedHashMap&amp;lt;&amp;gt;();
        e.getBindingResult().getFieldErrors()
                .forEach(fe -&amp;gt; fieldErrors.put(fe.getField(), fe.getDefaultMessage()));

        Map&amp;lt;String, Object&amp;gt; body = new LinkedHashMap&amp;lt;&amp;gt;();
        body.put(&quot;code&quot;, &quot;VALIDATION_ERROR&quot;);
        body.put(&quot;errors&quot;, fieldErrors);

        return ResponseEntity.badRequest().body(body);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답 예시(400)&lt;/p&gt;
&lt;pre id=&quot;code_1767249875615&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;code&quot;: &quot;VALIDATION_ERROR&quot;,
  &quot;errors&quot;: {
    &quot;userId&quot;: &quot;사용자 ID는 필수입니다&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-2. 비즈니스 예외(예: 중복 ID) &amp;rarr; 커스텀 예외 &amp;rarr; Advice에서 409로 변환&lt;/p&gt;
&lt;pre id=&quot;code_1767249993087&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class DuplicateUserIdException extends RuntimeException {
    public DuplicateUserIdException(String msg) { super(msg); }
}

@Service
public class MemberService {
    public void signup(SignupRequest req) {
        if (/*이미 존재*/) throw new DuplicateUserIdException(&quot;이미 사용 중인 ID입니다&quot;);
    }
}

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(DuplicateUserIdException.class)
    public ResponseEntity&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; handleDup(DuplicateUserIdException e) {
        return ResponseEntity.status(409).body(Map.of(&quot;code&quot;,&quot;DUPLICATE_ID&quot;,&quot;message&quot;, e.getMessage()));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1108&quot; data-start=&quot;1078&quot; data-ke-size=&quot;size26&quot;&gt;3) Spring Boot 기본(자동) 예외 응답&lt;/h2&gt;
&lt;p data-end=&quot;1137&quot; data-start=&quot;1109&quot; data-ke-size=&quot;size16&quot;&gt;별도 처리 안 하면 Boot가 기본 에러 핸들러로:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1278&quot; data-start=&quot;1138&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1179&quot; data-start=&quot;1138&quot;&gt;HTML(브라우저) 또는 JSON(API 요청) 형태로 에러 응답 생성&lt;/li&gt;
&lt;li data-end=&quot;1278&quot; data-start=&quot;1180&quot;&gt;BasicErrorController + ErrorAttributes 기반&lt;br /&gt;(보통 API 서버면 이 기본 응답 대신, Advice로 &amp;ldquo;에러 포맷&amp;rdquo;을 통일합니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3-1. 예: 존재하지 않는 URL 호출&lt;/p&gt;
&lt;pre id=&quot;code_1767250018839&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;timestamp&quot;: &quot;2026-01-01T06:00:00.000+00:00&quot;,
  &quot;status&quot;: 404,
  &quot;error&quot;: &quot;Not Found&quot;,
  &quot;path&quot;: &quot;/no-such-api&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1309&quot; data-start=&quot;1285&quot; data-ke-size=&quot;size26&quot;&gt;4) 필터/인터셉터/시큐리티에서의 예외&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1483&quot; data-start=&quot;1310&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1371&quot; data-start=&quot;1310&quot;&gt;&lt;b&gt;Filter 체인&lt;/b&gt;에서 터진 예외는 @ControllerAdvice로 안 잡히는 경우가 많습니다.&lt;/li&gt;
&lt;li data-end=&quot;1483&quot; data-start=&quot;1372&quot;&gt;Spring Security는 보통:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1483&quot; data-start=&quot;1397&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1432&quot; data-start=&quot;1397&quot;&gt;인증 실패: AuthenticationEntryPoint&lt;/li&gt;
&lt;li data-end=&quot;1483&quot; data-start=&quot;1435&quot;&gt;인가 실패: AccessDeniedHandler&lt;br /&gt;쪽에서 응답을 만들어줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의사항:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4496&quot; data-start=&quot;4443&quot;&gt;**@ControllerAdvice는 &amp;lsquo;컨트롤러까지 들어온 예외&amp;rsquo;**를 주로 처리합니다.&lt;/li&gt;
&lt;li data-end=&quot;4545&quot; data-start=&quot;4497&quot;&gt;&lt;b&gt;필터(Filter)에서 터진 예외는 Advice가 못 잡는 경우가 많습니다.&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;4596&quot; data-start=&quot;4546&quot;&gt;Spring Security는 자체 진입점/핸들러로 응답을 만드는 흐름이 일반적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4-1. Filter에서 예외가 터짐 &amp;rarr; Advice로 안 잡힘(대표 케이스)&lt;/p&gt;
&lt;pre id=&quot;code_1767250078832&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class ApiKeyFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
            throws ServletException, IOException {

        String apiKey = req.getHeader(&quot;X-API-KEY&quot;);
        if (apiKey == null) {
            throw new IllegalStateException(&quot;API KEY 없음&quot;); // ⚠️ 여기서 터지면 ControllerAdvice가 못 받을 수 있음
        }
        chain.doFilter(req, res);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 패턴(필터에서 직접 응답 작성)&lt;/p&gt;
&lt;pre id=&quot;code_1767250090013&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (apiKey == null) {
    res.setStatus(401);
    res.setContentType(&quot;application/json;charset=UTF-8&quot;);
    res.getWriter().write(&quot;{\&quot;code\&quot;:\&quot;UNAUTHORIZED\&quot;,\&quot;message\&quot;:\&quot;API KEY가 필요합니다\&quot;}&quot;);
    return;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4-2. Interceptor에서 예외/차단 처리&lt;/p&gt;
&lt;pre id=&quot;code_1767250105190&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class AdminInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
        if (!&quot;admin&quot;.equals(req.getHeader(&quot;X-ROLE&quot;))) {
            res.setStatus(403);
            res.setContentType(&quot;application/json;charset=UTF-8&quot;);
            res.getWriter().write(&quot;{\&quot;code\&quot;:\&quot;FORBIDDEN\&quot;,\&quot;message\&quot;:\&quot;권한 없음\&quot;}&quot;);
            return false; // ✅ 여기서 요청 중단 (컨트롤러 미진입)
        }
        return true;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록:&lt;/p&gt;
&lt;pre id=&quot;code_1767250118662&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class WebConfig implements WebMvcConfigurer {
    private final AdminInterceptor adminInterceptor;

    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(adminInterceptor).addPathPatterns(&quot;/api/admin/**&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4-3. Spring Security: 인증/인가 예외는 보통 여기서 응답을 만듦&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;6470&quot; data-start=&quot;6429&quot;&gt;인증 안 됨(401): AuthenticationEntryPoint&lt;/li&gt;
&lt;li data-end=&quot;6506&quot; data-start=&quot;6471&quot;&gt;권한 없음(403): AccessDeniedHandler&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1767250147080&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest req, HttpServletResponse res, AuthenticationException ex)
            throws IOException {
        res.setStatus(401);
        res.setContentType(&quot;application/json;charset=UTF-8&quot;);
        res.getWriter().write(&quot;{\&quot;code\&quot;:\&quot;UNAUTHORIZED\&quot;,\&quot;message\&quot;:\&quot;로그인이 필요합니다\&quot;}&quot;);
    }
}

@Component
public class RestAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest req, HttpServletResponse res, AccessDeniedException ex)
            throws IOException {
        res.setStatus(403);
        res.setContentType(&quot;application/json;charset=UTF-8&quot;);
        res.getWriter().write(&quot;{\&quot;code\&quot;:\&quot;FORBIDDEN\&quot;,\&quot;message\&quot;:\&quot;접근 권한이 없습니다\&quot;}&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Security 설정에 연결:&lt;/p&gt;
&lt;pre id=&quot;code_1767250159573&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http,
        RestAuthenticationEntryPoint entryPoint,
        RestAccessDeniedHandler deniedHandler) throws Exception {

    return http
        .csrf(csrf -&amp;gt; csrf.disable())
        .exceptionHandling(ex -&amp;gt; ex
            .authenticationEntryPoint(entryPoint)
            .accessDeniedHandler(deniedHandler)
        )
        .authorizeHttpRequests(auth -&amp;gt; auth
            .requestMatchers(&quot;/api/admin/**&quot;).hasRole(&quot;ADMIN&quot;)
            .anyRequest().permitAll()
        )
        .build();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 해당 예외 처리를 실무에서 사용하지 못할 경우.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;132&quot; data-start=&quot;92&quot; data-ke-size=&quot;size26&quot;&gt;1) 트랜잭션 롤백 규칙을 모르면 생기는 문제(정합성/데이터 오염)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;467&quot; data-start=&quot;133&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;233&quot; data-start=&quot;133&quot;&gt;&lt;b&gt;부분 저장(Partial Commit)&lt;/b&gt;&lt;br /&gt;예: 회원 저장은 됐는데, 권한/프로필/연관 테이블 저장 중 예외 &amp;rarr; 롤백이 안 돼서 DB에 &amp;ldquo;반쯤 생성된 회원&amp;rdquo;이 남음.&lt;/li&gt;
&lt;li data-end=&quot;313&quot; data-start=&quot;234&quot;&gt;&lt;b&gt;체크 예외로 인해 커밋되는 사고&lt;/b&gt;&lt;br /&gt;체크 예외는 기본 롤백이 아니라서, &amp;ldquo;실패했다고 생각했는데 DB엔 반영&amp;rdquo;되는 케이스가 생김.&lt;/li&gt;
&lt;li data-end=&quot;398&quot; data-start=&quot;314&quot;&gt;&lt;b&gt;try-catch로 예외를 삼켜서 커밋&lt;/b&gt;&lt;br /&gt;에러를 로그만 찍고 정상 리턴 &amp;rarr; 스프링은 성공으로 보고 커밋 &amp;rarr; 나중에 데이터 꼬임이 누적.&lt;/li&gt;
&lt;li data-end=&quot;467&quot; data-start=&quot;399&quot;&gt;&lt;b&gt;재처리/복구 비용 증가&lt;/b&gt;&lt;br /&gt;데이터가 한번 오염되면 운영에서 수작업 수정, 배치 복구, 고객 응대가 필요해짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;526&quot; data-start=&quot;474&quot; data-ke-size=&quot;size26&quot;&gt;2) MVC/REST 예외를 응답으로 변환 못하면 생기는 문제(프론트/QA/사용자 경험)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;880&quot; data-start=&quot;527&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;644&quot; data-start=&quot;527&quot;&gt;&lt;b&gt;에러 응답 포맷이 제각각&lt;/b&gt;&lt;br /&gt;어떤 API는 {message}, 어떤 API는 Boot 기본 {status,error,path}&amp;hellip;&lt;br /&gt;&amp;rarr; 프론트가 케이스별 파싱 분기 지옥 + 버그 증가.&lt;/li&gt;
&lt;li data-end=&quot;735&quot; data-start=&quot;645&quot;&gt;&lt;b&gt;필드 검증 에러를 제대로 못 줌&lt;/b&gt;&lt;br /&gt;&amp;ldquo;아이디가 비었어요&amp;rdquo; 같은 정확한 안내 대신 그냥 500/400만 떨어짐&lt;br /&gt;&amp;rarr; 사용자 이탈 + CS 증가.&lt;/li&gt;
&lt;li data-end=&quot;813&quot; data-start=&quot;736&quot;&gt;&lt;b&gt;상태코드가 엉망&lt;/b&gt;&lt;br /&gt;중복인데 500, 권한없는데 200+메시지&amp;hellip;&lt;br /&gt;&amp;rarr; 프론트가 정상/에러 판단 못해서 화면/로직 꼬임.&lt;/li&gt;
&lt;li data-end=&quot;880&quot; data-start=&quot;814&quot;&gt;&lt;b&gt;디버깅 난이도 상승&lt;/b&gt;&lt;br /&gt;QA가 재현해도 &amp;ldquo;왜 실패했는지&amp;rdquo; 정보가 없거나 매번 다름 &amp;rarr; 수정 속도 느려짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;942&quot; data-start=&quot;887&quot; data-ke-size=&quot;size26&quot;&gt;3) Spring Boot 기본(자동) 에러 응답에만 의존하면 생기는 문제(보안/일관성/운영)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1197&quot; data-start=&quot;943&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1033&quot; data-start=&quot;943&quot;&gt;&lt;b&gt;환경에 따라 메시지 노출이 달라짐&lt;/b&gt;&lt;br /&gt;개발/운영 설정에 따라 stacktrace/exception 메시지가 노출되거나 숨겨짐 &amp;rarr; 보안 리스크/혼선.&lt;/li&gt;
&lt;li data-end=&quot;1122&quot; data-start=&quot;1034&quot;&gt;&lt;b&gt;API 표준 계약이 깨짐&lt;/b&gt;&lt;br /&gt;&amp;ldquo;우리 API는 항상 code/message/errors 준다&amp;rdquo; 같은 계약을 못 지킴 &amp;rarr; 프론트/외부 연동 불안정.&lt;/li&gt;
&lt;li data-end=&quot;1197&quot; data-start=&quot;1123&quot;&gt;&lt;b&gt;로그/모니터링과 연결이 어려움&lt;/b&gt;&lt;br /&gt;에러코드 체계가 없으니 대시보드에서 원인 분류(중복/검증/권한/서버오류)가 힘들어짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1251&quot; data-start=&quot;1204&quot; data-ke-size=&quot;size26&quot;&gt;4) 필터/인터셉터/시큐리티 예외를 다루지 못하면 생기는 문제(인증/인가/장애)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1619&quot; data-start=&quot;1252&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1332&quot; data-start=&quot;1252&quot;&gt;&lt;b&gt;401/403이 아니라 500이 나감&lt;/b&gt;&lt;br /&gt;인증 실패가 서버 장애처럼 보이고, 프론트는 &amp;ldquo;로그인 유도&amp;rdquo; 대신 &amp;ldquo;오류 화면&amp;rdquo;을 띄움.&lt;/li&gt;
&lt;li data-end=&quot;1416&quot; data-start=&quot;1333&quot;&gt;&lt;b&gt;CORS/프리플라이트가 깨짐&lt;/b&gt;&lt;br /&gt;필터에서 예외나 응답을 잘못 만들면 OPTIONS 요청이 실패 &amp;rarr; 브라우저에서 아예 호출 자체가 막힘.&lt;/li&gt;
&lt;li data-end=&quot;1524&quot; data-start=&quot;1417&quot;&gt;&lt;b&gt;Security 예외 응답 포맷 불일치&lt;/b&gt;&lt;br /&gt;일반 API는 {code,message}인데, 인증 실패만 기본 포맷/HTML로 내려감 &amp;rarr; 프론트 처리 실패(토큰 만료 처리 등).&lt;/li&gt;
&lt;li data-end=&quot;1619&quot; data-start=&quot;1525&quot;&gt;&lt;b&gt;응답이 이미 커밋된 뒤 예외&lt;/b&gt;&lt;br /&gt;필터/시큐리티 체인에서 예외 처리 미흡하면 &amp;ldquo;응답을 쓰다가 또 예외&amp;rdquo;가 터져 로그만 남고 클라이언트는 애매한 응답을 받음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* try-catch에 과하게 의존하던가 비즈니스 로직에서 예외를 하나하나 처리할 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;96&quot; data-start=&quot;82&quot; data-ke-size=&quot;size26&quot;&gt;1) 코드 품질 문제&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;417&quot; data-start=&quot;97&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;185&quot; data-start=&quot;97&quot;&gt;&lt;b&gt;중복 폭발&lt;/b&gt;: 모든 메서드에 try { ... } catch (...) { ... }가 깔리면서 같은 응답 생성/로그/메시지 처리 코드가 반복됨.&lt;/li&gt;
&lt;li data-end=&quot;270&quot; data-start=&quot;186&quot;&gt;&lt;b&gt;관심사 섞임&lt;/b&gt;: 원래 로직(회원가입/주문/결제) + 에러 응답 포맷팅 + 로깅 + 상태코드 결정이 한 덩어리가 됨 &amp;rarr; 유지보수 난이도 급상승.&lt;/li&gt;
&lt;li data-end=&quot;345&quot; data-start=&quot;271&quot;&gt;&lt;b&gt;읽기/리뷰 난이도 증가&lt;/b&gt;: 핵심 로직이 try-catch에 묻혀서 &amp;ldquo;이 API가 뭘 하는지&amp;rdquo; 파악이 느려짐. 실수도 늘어남.&lt;/li&gt;
&lt;li data-end=&quot;417&quot; data-start=&quot;346&quot;&gt;&lt;b&gt;일관성 붕괴&lt;/b&gt;: 어떤 곳은 400, 어떤 곳은 409, 어떤 곳은 200+message&amp;hellip; 팀원이 늘수록 더 엉망이 됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;443&quot; data-start=&quot;419&quot; data-ke-size=&quot;size26&quot;&gt;2) 기능/정합성 문제(특히 트랜잭션)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;655&quot; data-start=&quot;444&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;530&quot; data-start=&quot;444&quot;&gt;&lt;b&gt;예외 삼킴으로 커밋&lt;/b&gt;: 서비스에서 예외를 잡고 정상 리턴하면 스프링은 성공으로 보고 트랜잭션이 커밋될 수 있음 &amp;rarr; &amp;ldquo;반쯤 저장된 데이터&amp;rdquo;가 남음.&lt;/li&gt;
&lt;li data-end=&quot;586&quot; data-start=&quot;531&quot;&gt;&lt;b&gt;롤백 조건 누락&lt;/b&gt;: 체크 예외/특정 예외에서 롤백이 필요해도 각 메서드에서 빠뜨리기 쉬움.&lt;/li&gt;
&lt;li data-end=&quot;655&quot; data-start=&quot;587&quot;&gt;&lt;b&gt;복구 불가능한 데이터 오염&lt;/b&gt;: 한번 꼬인 데이터는 나중에 발견되고, 수정 비용이 훨씬 큼(수동 수정/배치/CS).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;674&quot; data-start=&quot;657&quot; data-ke-size=&quot;size26&quot;&gt;3) 장애 대응/운영 문제&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;926&quot; data-start=&quot;675&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;788&quot; data-start=&quot;675&quot;&gt;&lt;b&gt;로그 품질이 나빠짐&lt;/b&gt;: 여기저기서 각각 log.error(...) 찍다 보면
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;788&quot; data-start=&quot;727&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;752&quot; data-start=&quot;727&quot;&gt;같은 에러가 여러 번 찍히거나(중복 로그)&lt;/li&gt;
&lt;li data-end=&quot;788&quot; data-start=&quot;755&quot;&gt;반대로 중요한 맥락(요청ID/사용자/에러코드)이 누락됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;864&quot; data-start=&quot;789&quot;&gt;&lt;b&gt;모니터링/통계가 안 됨&lt;/b&gt;: 전역 에러코드/상태코드 체계가 없으니 &amp;ldquo;검증오류가 많은지&amp;rdquo;, &amp;ldquo;권한오류 폭증인지&amp;rdquo;를 지표로 못 봄.&lt;/li&gt;
&lt;li data-end=&quot;926&quot; data-start=&quot;865&quot;&gt;&lt;b&gt;버그 수정 속도 저하&lt;/b&gt;: 에러 처리 로직이 분산돼 있어서 수정하려면 파일을 수십 군데 찾아다녀야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;952&quot; data-start=&quot;928&quot; data-ke-size=&quot;size26&quot;&gt;4) 클라이언트(프론트/앱) 연동 문제&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1179&quot; data-start=&quot;953&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1016&quot; data-start=&quot;953&quot;&gt;&lt;b&gt;응답 포맷이 들쭉날쭉&lt;/b&gt;: 프론트가 케이스별 파싱 분기를 계속 추가하게 됨 &amp;rarr; 프론트 버그/개발기간 증가.&lt;/li&gt;
&lt;li data-end=&quot;1104&quot; data-start=&quot;1017&quot;&gt;&lt;b&gt;HTTP 의미가 깨짐&lt;/b&gt;: &amp;ldquo;중복&amp;rdquo;인데 500, &amp;ldquo;권한 없음&amp;rdquo;인데 200&amp;hellip;&lt;br /&gt;&amp;rarr; 재시도/로그아웃/토스트 메시지 같은 UX 처리를 제대로 못 함.&lt;/li&gt;
&lt;li data-end=&quot;1179&quot; data-start=&quot;1105&quot;&gt;&lt;b&gt;국제화/메시지 정책 관리 불가&lt;/b&gt;: 메시지를 여기저기 하드코딩하면 문구 수정, 정책 변경(노출 수준 조정)이 거의 불가능해짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1198&quot; data-start=&quot;1181&quot; data-ke-size=&quot;size26&quot;&gt;5) 보안/정보노출 리스크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1365&quot; data-start=&quot;1199&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1288&quot; data-start=&quot;1199&quot;&gt;&lt;b&gt;예외 메시지 그대로 노출&lt;/b&gt;: try-catch에서 e.getMessage()를 그대로 내려주면 내부 테이블명/쿼리/스택 정보가 새어 나갈 수 있음.&lt;/li&gt;
&lt;li data-end=&quot;1365&quot; data-start=&quot;1289&quot;&gt;&lt;b&gt;필터/시큐리티 예외 대응 실패&lt;/b&gt;: 인증/인가에서 발생한 예외를 컨트롤러에서 처리하려다 못 잡고 500으로 터지는 케이스가 생김.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1382&quot; data-start=&quot;1367&quot; data-ke-size=&quot;size26&quot;&gt;6) 테스트/확장 문제&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1536&quot; data-start=&quot;1383&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1463&quot; data-start=&quot;1383&quot;&gt;&lt;b&gt;테스트가 무의미해짐&lt;/b&gt;: 전역 처리라면 &amp;ldquo;예외 &amp;rarr; 어떤 응답&amp;rdquo;이 일관되게 검증 가능한데, 분산 처리면 케이스마다 테스트를 새로 짜야 함.&lt;/li&gt;
&lt;li data-end=&quot;1536&quot; data-start=&quot;1464&quot;&gt;&lt;b&gt;요구사항 변경에 취약&lt;/b&gt;: &amp;ldquo;에러 포맷에 traceId 추가&amp;rdquo; 같은 변경이 오면 전역은 1곳 수정인데, 분산은 전부 수정.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 try-catch로 &amp;ldquo;개별 대응&amp;rdquo;하면 단기적으로는 빨라 보이지만, 실무에서는 &lt;b&gt;정합성 사고(커밋/롤백)&lt;/b&gt; + &lt;b&gt;응답/상태코드 불일치&lt;/b&gt; + &lt;b&gt;유지보수 지옥&lt;/b&gt;으로 비용이 크게 늘어납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* try-catch를 사용해도 괜찮은것.&lt;/p&gt;
&lt;h3 data-end=&quot;184&quot; data-start=&quot;153&quot; data-ke-size=&quot;size23&quot;&gt;A. 외부 경계(네트워크/파일/외부 시스템) 호출&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;294&quot; data-start=&quot;185&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;241&quot; data-start=&quot;185&quot;&gt;HTTP 호출(WebClient/RestTemplate), 메시지큐, 파일 IO, S3 업로드 등&lt;/li&gt;
&lt;li data-end=&quot;294&quot; data-start=&quot;242&quot;&gt;이유: 여기서 터지는 예외는 &lt;b&gt;도메인(비즈니스) 예외로 치환&lt;/b&gt;해야 내부 로직이 깔끔해짐&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1767280511798&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
    externalClient.send(...);
} catch (TimeoutException e) {
    throw new ExternalTimeoutException(&quot;외부 시스템 타임아웃&quot;, e);
} catch (Exception e) {
    throw new ExternalSystemException(&quot;외부 시스템 오류&quot;, e);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;631&quot; data-start=&quot;521&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 그리고 @RestControllerAdvice에서 ExternalTimeoutException은 504, ExternalSystemException은 502/500 같은 식으로 통일.&lt;/p&gt;
&lt;h3 data-end=&quot;676&quot; data-start=&quot;633&quot; data-ke-size=&quot;size23&quot;&gt;B. 리소스 정리 / 보장 (close, unlock, 임시파일 삭제)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;750&quot; data-start=&quot;677&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;706&quot; data-start=&quot;677&quot;&gt;try-with-resources 사용이 정석&lt;/li&gt;
&lt;li data-end=&quot;750&quot; data-start=&quot;707&quot;&gt;이유: &amp;ldquo;실패해도 반드시 정리&amp;rdquo;가 목적이지, 에러 응답을 만들 목적이 아님&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;795&quot; data-start=&quot;752&quot; data-ke-size=&quot;size23&quot;&gt;C. &amp;ldquo;예외가 정상 흐름인&amp;rdquo; 케이스를 명확히 다룰 때(아주 제한적으로)&lt;/h3&gt;
&lt;p data-end=&quot;816&quot; data-start=&quot;796&quot; data-ke-size=&quot;size16&quot;&gt;예: 캐시 읽기 실패하면 DB로 폴백&lt;/p&gt;
&lt;pre id=&quot;code_1767280532200&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
    return cache.get(key);
} catch (Exception e) {
    log.warn(&quot;캐시 실패, DB로 폴백&quot;, e);
    return repo.find(...);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-end=&quot;1034&quot; data-start=&quot;992&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*try- catch를 쓰면 안되는 부분.&lt;/p&gt;
&lt;h3 data-end=&quot;1075&quot; data-start=&quot;1035&quot; data-ke-size=&quot;size23&quot;&gt;A. 컨트롤러에서 비즈니스 예외를 try-catch로 응답 만들기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1154&quot; data-start=&quot;1076&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1101&quot; data-start=&quot;1076&quot;&gt;문제: 포맷/상태코드가 컨트롤러마다 달라짐&lt;/li&gt;
&lt;li data-end=&quot;1154&quot; data-start=&quot;1102&quot;&gt;정석: 컨트롤러는 얇게, 예외는 @RestControllerAdvice가 응답으로 변환&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1183&quot; data-start=&quot;1156&quot; data-ke-size=&quot;size23&quot;&gt;B. 서비스에서 예외를 잡고 &amp;ldquo;정상 리턴&amp;rdquo;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1248&quot; data-start=&quot;1184&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1212&quot; data-start=&quot;1184&quot;&gt;문제: 트랜잭션이 커밋될 수 있음(데이터 오염)&lt;/li&gt;
&lt;li data-end=&quot;1248&quot; data-start=&quot;1213&quot;&gt;정석: 서비스는 &lt;b&gt;예외를 던져서 실패를 명확히&lt;/b&gt; 만들 것&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1286&quot; data-start=&quot;1250&quot; data-ke-size=&quot;size23&quot;&gt;C. &amp;ldquo;모든 예외 catch(Exception)&amp;rdquo;로 뭉개기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1361&quot; data-start=&quot;1287&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1322&quot; data-start=&quot;1287&quot;&gt;문제: 원인 분류 불가 + 장애 탐지 늦어짐 + 디버깅 지옥&lt;/li&gt;
&lt;li data-end=&quot;1361&quot; data-start=&quot;1323&quot;&gt;정석: 외부 경계에서만 필요한 범위로 잡고, 내부는 타입별로 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 예시&lt;/h2&gt;
&lt;h3 data-end=&quot;75&quot; data-start=&quot;68&quot; data-ke-size=&quot;size23&quot;&gt;시나리오&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;185&quot; data-start=&quot;76&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;101&quot; data-start=&quot;76&quot;&gt;요청: POST /api/members&lt;/li&gt;
&lt;li data-end=&quot;124&quot; data-start=&quot;102&quot;&gt;검증: userId 필수, 4~20자&lt;/li&gt;
&lt;li data-end=&quot;152&quot; data-start=&quot;125&quot;&gt;비즈니스: userId 중복이면 실패(409)&lt;/li&gt;
&lt;li data-end=&quot;185&quot; data-start=&quot;153&quot;&gt;외부: 이메일 인증코드 발송(외부 시스템, 실패 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권장 패턴&lt;/p&gt;
&lt;p data-end=&quot;217&quot; data-start=&quot;211&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;원칙&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;339&quot; data-start=&quot;218&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;242&quot; data-start=&quot;218&quot;&gt;컨트롤러: try-catch 없음, 얇게&lt;/li&gt;
&lt;li data-end=&quot;266&quot; data-start=&quot;243&quot;&gt;서비스: 비즈니스 예외는 던짐(런타임)&lt;/li&gt;
&lt;li data-end=&quot;302&quot; data-start=&quot;267&quot;&gt;외부 호출은 try-catch로 &lt;b&gt;치환(변환) 후 던짐&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;339&quot; data-start=&quot;303&quot;&gt;응답 포맷은 @RestControllerAdvice가 통일&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) DTO&lt;/p&gt;
&lt;pre id=&quot;code_1767283169272&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SignupRequest {
    @NotBlank(message = &quot;사용자 ID는 필수입니다&quot;)
    @Size(min = 4, max = 20, message = &quot;사용자 ID는 4~20자&quot;)
    private String userId;

    @NotBlank(message = &quot;이메일은 필수입니다&quot;)
    private String email;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 컨트롤러(try-catch 없음)&lt;/p&gt;
&lt;pre id=&quot;code_1767283182575&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@PostMapping(&quot;/api/members&quot;)
public ResponseEntity&amp;lt;Void&amp;gt; signup(@Valid @RequestBody SignupRequest req) {
    memberService.signup(req);
    return ResponseEntity.status(201).build();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 서비스(비즈니스 규칙 + 외부 호출은 치환)&lt;/p&gt;
&lt;pre id=&quot;code_1767283196413&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@RequiredArgsConstructor
public class MemberService {
    private final MemberRepository memberRepository;
    private final MailClient mailClient;

    @Transactional
    public void signup(SignupRequest req) {
        if (memberRepository.existsByUserId(req.getUserId())) {
            throw new DuplicateUserIdException(&quot;이미 사용 중인 ID입니다&quot;);
        }

        memberRepository.save(new Member(req.getUserId(), req.getEmail()));

        // 외부 시스템: 실패 가능 &amp;rarr; try-catch로 도메인 예외로 치환 후 throw
        try {
            mailClient.sendVerificationCode(req.getEmail());
        } catch (Exception e) {
            throw new ExternalMailSendException(&quot;이메일 발송에 실패했습니다&quot;, e);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) 전역 예외 처리(응답 통일)&lt;/p&gt;
&lt;pre id=&quot;code_1767283209185&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity&amp;lt;?&amp;gt; handleValid(MethodArgumentNotValidException e) {
        Map&amp;lt;String, String&amp;gt; errors = new LinkedHashMap&amp;lt;&amp;gt;();
        e.getBindingResult().getFieldErrors()
                .forEach(fe -&amp;gt; errors.put(fe.getField(), fe.getDefaultMessage()));
        return ResponseEntity.badRequest().body(Map.of(&quot;code&quot;,&quot;VALIDATION_ERROR&quot;,&quot;errors&quot;,errors));
    }

    @ExceptionHandler(DuplicateUserIdException.class)
    public ResponseEntity&amp;lt;?&amp;gt; handleDup(DuplicateUserIdException e) {
        return ResponseEntity.status(409).body(Map.of(&quot;code&quot;,&quot;DUPLICATE_ID&quot;,&quot;message&quot;, e.getMessage()));
    }

    @ExceptionHandler(ExternalMailSendException.class)
    public ResponseEntity&amp;lt;?&amp;gt; handleExternal(ExternalMailSendException e) {
        return ResponseEntity.status(502).body(Map.of(&quot;code&quot;,&quot;MAIL_SEND_FAILED&quot;,&quot;message&quot;, e.getMessage()));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비권장&lt;/p&gt;
&lt;p data-end=&quot;2838&quot; data-start=&quot;2832&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2928&quot; data-start=&quot;2839&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2868&quot; data-start=&quot;2839&quot;&gt;컨트롤러에서 try-catch로 응답을 직접 만듦&lt;/li&gt;
&lt;li data-end=&quot;2902&quot; data-start=&quot;2869&quot;&gt;서비스에서 예외를 잡고 &amp;ldquo;정상 리턴&amp;rdquo; &amp;rarr; 커밋/오염 위험&lt;/li&gt;
&lt;li data-end=&quot;2928&quot; data-start=&quot;2903&quot;&gt;에러 포맷이 API마다 달라질 가능성 높음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 컨트롤러가 모든 걸 try-catch로 처리&lt;/p&gt;
&lt;pre id=&quot;code_1767283262160&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@PostMapping(&quot;/api/members&quot;)
public ResponseEntity&amp;lt;?&amp;gt; signup(@Valid @RequestBody SignupRequest req) {
    try {
        memberService.signup(req);
        return ResponseEntity.status(201).body(Map.of(&quot;message&quot;,&quot;ok&quot;));
    } catch (DuplicateUserIdException e) {
        return ResponseEntity.status(400).body(&quot;중복입니다&quot;); // ⚠️ 409가 아니라 400, 포맷도 문자열
    } catch (Exception e) {
        return ResponseEntity.status(200).body(&quot;실패했지만 200&quot;); // ⚠️ 상태코드 의미 붕괴
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;3439&quot; data-start=&quot;3436&quot; data-ke-size=&quot;size16&quot;&gt;문제점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3517&quot; data-start=&quot;3440&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3482&quot; data-start=&quot;3440&quot;&gt;중복인데 400, 심지어 어떤 곳은 200&amp;hellip; &lt;b&gt;클라이언트 로직 꼬임&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;3517&quot; data-start=&quot;3483&quot;&gt;응답 포맷이 문자열/객체 뒤섞임 &amp;rarr; 프론트 파싱 분기가 어려워짐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 서비스에서 예외 삼키기(정합성 사고 포인트)&lt;/p&gt;
&lt;pre id=&quot;code_1767283289494&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
public void signup(SignupRequest req) {
    try {
        if (memberRepository.existsByUserId(req.getUserId())) {
            throw new DuplicateUserIdException(&quot;중복&quot;);
        }
        memberRepository.save(new Member(req.getUserId(), req.getEmail()));
        mailClient.sendVerificationCode(req.getEmail());
    } catch (Exception e) {
        // ⚠️ 로그만 찍고 끝(또는 무시)
        // 여기서 메서드가 정상 종료되면 트랜잭션 커밋될 수 있음 &amp;rarr; &quot;반쯤 성공&quot; 데이터 생성
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;4018&quot; data-start=&quot;4015&quot; data-ke-size=&quot;size16&quot;&gt;문제점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4101&quot; data-start=&quot;4019&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4065&quot; data-start=&quot;4019&quot;&gt;외부 메일 실패가 발생해도 회원은 저장됨(정책이 그게 아니라면 &amp;ldquo;데이터 오염&amp;rdquo;)&lt;/li&gt;
&lt;li data-end=&quot;4101&quot; data-start=&quot;4066&quot;&gt;실패를 호출자가 알 수 없음(응답은 성공처럼 나갈 수 있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;4149&quot; data-start=&quot;4108&quot; data-ke-size=&quot;size26&quot;&gt;결론: try-catch는 &amp;ldquo;응답 만들기&amp;rdquo;가 아니라 &amp;ldquo;경계 처리&amp;rdquo;에만&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4247&quot; data-start=&quot;4150&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4184&quot; data-start=&quot;4150&quot;&gt;외부 호출 실패를 &lt;b&gt;의미 있는 도메인 예외로 변환&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;4199&quot; data-start=&quot;4185&quot;&gt;리소스 정리, 폴백&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;145&quot; data-start=&quot;111&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h3 data-end=&quot;218&quot; data-start=&quot;147&quot; data-ke-size=&quot;size23&quot;&gt;Q1. @Transactional에서 Runtime 예외는 롤백인데, Checked 예외는 왜 기본 롤백이 아닌가요?&lt;/h3&gt;
&lt;p data-end=&quot;382&quot; data-start=&quot;219&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; 스프링의 기본 정책은 &amp;ldquo;프로그래밍 오류/예상치 못한 실패(Runtime)&amp;rdquo;는 롤백, &amp;ldquo;업무적으로 예상 가능한 예외(Checked)&amp;rdquo;는 호출자가 처리할 수도 있다고 보고 커밋될 수 있게 둔 겁니다. 그래서 Checked 예외도 롤백이 필요하면 rollbackFor로 명시합니다.&lt;/p&gt;
&lt;h3 data-end=&quot;449&quot; data-start=&quot;389&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-end=&quot;449&quot; data-start=&quot;389&quot; data-ke-size=&quot;size23&quot;&gt;Q2. 서비스에서 try-catch로 예외를 잡아야 하는 경우에도 롤백이 되게 하려면 어떻게 하나요?&lt;/h3&gt;
&lt;p data-end=&quot;635&quot; data-start=&quot;450&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; ① 잡고 &lt;b&gt;다시 throw&lt;/b&gt;(도메인 예외로 치환해서 throw)하거나, ② 정말 잡고 끝내야 한다면 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()로 롤백 마킹합니다. 실무에서는 보통 &amp;ldquo;삼키지 말고 치환해서 던진다&amp;rdquo;를 기본으로 둡니다.&lt;/p&gt;
&lt;h3 data-end=&quot;697&quot; data-start=&quot;642&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-end=&quot;697&quot; data-start=&quot;642&quot; data-ke-size=&quot;size23&quot;&gt;Q3. self-invocation 때문에 트랜잭션이 안 걸리면 실무에서 어떻게 해결하나요?&lt;/h3&gt;
&lt;p data-end=&quot;868&quot; data-start=&quot;698&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; 가장 흔한 해결은 &lt;b&gt;구조 분리&lt;/b&gt;입니다. 트랜잭션이 필요한 메서드를 다른 서비스 빈으로 분리해서 &amp;ldquo;프록시를 거치게&amp;rdquo; 만듭니다. 또는 호출 경로를 컨트롤러&amp;rarr;서비스 빈으로 유지합니다(같은 클래스 내부 호출 피함). 특수하게 AspectJ weaving을 쓰는 방법도 있지만 일반적이지 않습니다.&lt;/p&gt;
&lt;h3 data-end=&quot;936&quot; data-start=&quot;875&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-end=&quot;936&quot; data-start=&quot;875&quot; data-ke-size=&quot;size23&quot;&gt;Q4. 전역 @RestControllerAdvice를 쓰면 모든 예외를 다 거기서 처리하면 되나요?&lt;/h3&gt;
&lt;p data-end=&quot;1114&quot; data-start=&quot;937&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; 원칙적으로는 &amp;ldquo;컨트롤러 레벨 예외&amp;rdquo;는 전역 Advice에서 통일하는 게 맞지만, &lt;b&gt;필터/시큐리티 체인에서 발생한 예외는 Advice 범위를 벗어나기 쉬워서&lt;/b&gt; 401/403 같은 건 Security 핸들러(EntryPoint/DeniedHandler)에서 처리합니다. 즉 &amp;ldquo;레이어별로 책임이 다릅니다.&amp;rdquo;&lt;/p&gt;
&lt;h3 data-end=&quot;1163&quot; data-start=&quot;1121&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-end=&quot;1163&quot; data-start=&quot;1121&quot; data-ke-size=&quot;size23&quot;&gt;Q5. 중복 ID 같은 비즈니스 예외를 409로 주는 이유는 뭔가요?&lt;/h3&gt;
&lt;p data-end=&quot;1311&quot; data-start=&quot;1164&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; 409 Conflict는 &amp;ldquo;현재 리소스 상태와 충돌&amp;rdquo;을 표현하기에 적합합니다(이미 존재하는 ID로 생성 시도). 400으로 처리해도 동작은 하지만, 상태코드를 의미 있게 쓰면 프론트가 예외를 더 정확히 분기(중복 안내/재시도 금지 등)할 수 있습니다.&lt;/p&gt;
&lt;h2 data-end=&quot;1349&quot; data-start=&quot;1318&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h3 data-end=&quot;1439&quot; data-start=&quot;1351&quot; data-ke-size=&quot;size23&quot;&gt;Q6. @Valid @RequestBody 말고 @RequestParam/@PathVariable 검증은 어떤 예외가 터지고 어떻게 처리하나요?&lt;/h3&gt;
&lt;p data-end=&quot;1636&quot; data-start=&quot;1440&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; @Validated를 클래스/컨트롤러에 붙이고 @Min, @NotBlank 같은 검증을 파라미터에 걸면 보통 **ConstraintViolationException**이 발생합니다. 전역 @RestControllerAdvice에서 이 예외를 잡아 400으로 통일하고, violation 정보를 원하는 포맷으로 내려줍니다.&lt;/p&gt;
&lt;h3 data-end=&quot;1706&quot; data-start=&quot;1643&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-end=&quot;1706&quot; data-start=&quot;1643&quot; data-ke-size=&quot;size23&quot;&gt;Q7. JSON이 깨졌거나 타입이 안 맞으면(예: age에 &quot;abc&quot;) 검증 예외가 아니라 뭐가 터지나요?&lt;/h3&gt;
&lt;p data-end=&quot;1872&quot; data-start=&quot;1707&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; 컨트롤러 진입 전 Jackson 파싱 단계에서 실패하므로 보통 &lt;b&gt;HttpMessageNotReadableException&lt;/b&gt;(또는 그 내부 cause)이 발생합니다. 이건 검증 실패와 성격이 달라서 &amp;ldquo;요청 바디 형식 오류&amp;rdquo;로 400을 내려주고 메시지는 일반화하는 게 안전합니다.&lt;/p&gt;
&lt;h3 data-end=&quot;1955&quot; data-start=&quot;1879&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-end=&quot;1955&quot; data-start=&quot;1879&quot; data-ke-size=&quot;size23&quot;&gt;Q8. 전역 핸들러에 @ExceptionHandler(Exception.class) 같은 마지막 처리기를 두면 뭐가 위험하죠?&lt;/h3&gt;
&lt;p data-end=&quot;2123&quot; data-start=&quot;1956&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; 너무 넓게 잡으면 원인 분류가 어려워지고(모든 게 500), 실수로 &lt;b&gt;비즈니스 예외까지 500으로 덮어버릴 수&lt;/b&gt; 있습니다. 그래서 &amp;ldquo;구체 예외 핸들러를 위에 두고&amp;rdquo;, 마지막 핸들러는 &lt;b&gt;진짜 예측 불가 오류만&lt;/b&gt; 처리하게 하며, 응답 메시지는 일반화하고 상세 원인은 로그로 남깁니다.&lt;/p&gt;
&lt;h3 data-end=&quot;2198&quot; data-start=&quot;2130&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-end=&quot;2198&quot; data-start=&quot;2130&quot; data-ke-size=&quot;size23&quot;&gt;Q9. 컨트롤러에서 응답 바디를 직접 쓰는 것 말고 &amp;ldquo;표준 에러 응답 DTO&amp;rdquo;를 만들어서 통일하려면 어떻게 하나요?&lt;/h3&gt;
&lt;p data-end=&quot;2387&quot; data-start=&quot;2199&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; ErrorResponse(code, message, errors, timestamp, path, traceId) 같은 DTO를 만들고, 전역 핸들러와 Security 핸들러(EntryPoint/DeniedHandler)가 &lt;b&gt;모두 동일 DTO를 반환&lt;/b&gt;하게 맞춥니다. 이렇게 하면 프론트가 항상 같은 규격으로 처리합니다.&lt;/p&gt;
&lt;h3 data-end=&quot;2463&quot; data-start=&quot;2394&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-end=&quot;2463&quot; data-start=&quot;2394&quot; data-ke-size=&quot;size23&quot;&gt;Q10. 외부 메일 발송이 실패했을 때, &amp;ldquo;회원 저장은 커밋하고 메일은 나중에 재시도&amp;rdquo;하고 싶으면 어떻게 설계하나요?&lt;/h3&gt;
&lt;p data-end=&quot;2551&quot; data-start=&quot;2464&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A.&lt;/b&gt; 같은 트랜잭션 안에서 외부 호출 실패를 throw하면 저장이 롤백됩니다. 커밋을 유지하려면 &lt;b&gt;트랜잭션을 분리&lt;/b&gt;해야 합니다. 대표 선택지는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2679&quot; data-start=&quot;2552&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2589&quot; data-start=&quot;2552&quot;&gt;회원 저장 커밋 후 **이벤트 발행(비동기)**로 메일 발송&lt;/li&gt;
&lt;li data-end=&quot;2679&quot; data-start=&quot;2590&quot;&gt;장애 내구성이 더 필요하면 &lt;b&gt;아웃박스 패턴&lt;/b&gt;(DB에 발송 작업을 기록하고 워커가 처리)&lt;br /&gt;즉 &amp;ldquo;DB 정합성 트랜잭션&amp;rdquo;과 &amp;ldquo;외부 부수효과&amp;rdquo;를 분리합니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Daily Dev Q&amp;amp;A</category>
      <author>awake123</author>
      <guid isPermaLink="true">https://awake123.tistory.com/44</guid>
      <comments>https://awake123.tistory.com/44#entry44comment</comments>
      <pubDate>Tue, 30 Dec 2025 17:44:31 +0900</pubDate>
    </item>
    <item>
      <title>스프링 프레임 워크</title>
      <link>https://awake123.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. 스프링 프레임 워크란&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**스프링 프레임워크(Spring Framework)**는&lt;br /&gt;&lt;b&gt;자바(Java) 기반 엔터프라이즈 애플리케이션을 효율적으로 개발하기 위한 오픈소스 프레임워크&lt;/b&gt;입니다.&lt;br /&gt;핵심 목적은 &lt;b&gt;객체 간 결합도를 낮추고(유연성&amp;uarr;), 반복 작업을 줄이며(생산성&amp;uarr;), 테스트와 유지보수를 쉽게&lt;/b&gt; 만드는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;304&quot; data-start=&quot;262&quot; data-ke-size=&quot;size23&quot;&gt;1) IoC (Inversion of Control, 제어의 역전)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;387&quot; data-start=&quot;305&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;332&quot; data-start=&quot;305&quot;&gt;객체를 &lt;b&gt;개발자가 직접 new 하지 않음&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;360&quot; data-start=&quot;333&quot;&gt;스프링 컨테이너가 객체 생성과 생명주기를 관리&lt;/li&gt;
&lt;li data-end=&quot;387&quot; data-start=&quot;361&quot;&gt;코드가 프레임워크에 덜 의존 &amp;rarr; 변경에 강함&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766470877264&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class ServiceA {
    // 객체 생성 안 함
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;502&quot; data-start=&quot;461&quot; data-ke-size=&quot;size23&quot;&gt;2) DI (Dependency Injection, 의존성 주입)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;548&quot; data-start=&quot;503&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;531&quot; data-start=&quot;503&quot;&gt;객체가 필요한 의존 객체를 &lt;b&gt;외부에서 주입&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;548&quot; data-start=&quot;532&quot;&gt;결합도 감소, 테스트 용이&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766470897800&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RequiredArgsConstructor
@Service
public class OrderService {
    private final OrderRepository orderRepository;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;724&quot; data-start=&quot;683&quot; data-ke-size=&quot;size23&quot;&gt;3) AOP (Aspect Oriented Programming)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;768&quot; data-start=&quot;725&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;740&quot; data-start=&quot;725&quot;&gt;&lt;b&gt;공통 관심사 분리&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;768&quot; data-start=&quot;741&quot;&gt;로깅, 트랜잭션, 보안 등을 핵심 로직과 분리&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766470916919&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
public void save() { }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1069&quot; data-start=&quot;1053&quot; data-ke-size=&quot;size26&quot;&gt;스프링 vs 스프링 부트&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1231&quot; data-start=&quot;1071&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1138&quot; data-start=&quot;1071&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;Spring Framework&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1138&quot; data-start=&quot;1117&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1138&quot; data-start=&quot;1117&quot;&gt;기능이 많고 유연하지만 설정이 복잡&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1231&quot; data-start=&quot;1139&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;Spring Boot&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1231&quot; data-start=&quot;1185&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1207&quot; data-start=&quot;1185&quot;&gt;스프링을 &lt;b&gt;쉽게 쓰게 만든 도구&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1231&quot; data-start=&quot;1210&quot;&gt;자동 설정, 내장 서버, 빠른 시작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1312&quot; data-start=&quot;1289&quot; data-ke-size=&quot;size26&quot;&gt;왜 스프링을 쓰는가 (현실적인 이유)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1398&quot; data-start=&quot;1314&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1328&quot; data-start=&quot;1314&quot;&gt;대규모 서비스에 검증됨&lt;/li&gt;
&lt;li data-end=&quot;1343&quot; data-start=&quot;1329&quot;&gt;유지보수&amp;middot;테스트에 강함&lt;/li&gt;
&lt;li data-end=&quot;1377&quot; data-start=&quot;1344&quot;&gt;생태계 방대 (JPA, Security, Batch 등)&lt;/li&gt;
&lt;li data-end=&quot;1398&quot; data-start=&quot;1378&quot;&gt;국내&amp;middot;해외 채용 시장 사실상 표준&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 스프링을 실무에서 사용하지 못한다면.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;스프링을 실무에서 사용하지 못한다면 &amp;rarr; 백엔드 개발자로서 실무 투입이 매우 어렵다&amp;rdquo;&lt;/b&gt;&lt;br /&gt;특히 &lt;b&gt;국내 Java 백엔드 시장에서는 거의 불가능에 가깝습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유로는&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;183&quot; data-start=&quot;159&quot; data-ke-size=&quot;size23&quot;&gt;1) 실무 요구 자체가 스프링 전제&lt;/h3&gt;
&lt;p data-end=&quot;219&quot; data-start=&quot;184&quot; data-ke-size=&quot;size16&quot;&gt;대부분 채용 공고는 암묵적으로 아래를 &lt;b&gt;기본값&lt;/b&gt;으로 둡니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;296&quot; data-start=&quot;221&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;234&quot; data-start=&quot;221&quot;&gt;Spring Boot&lt;/li&gt;
&lt;li data-end=&quot;247&quot; data-start=&quot;235&quot;&gt;Spring MVC&lt;/li&gt;
&lt;li data-end=&quot;265&quot; data-start=&quot;248&quot;&gt;Spring Data JPA&lt;/li&gt;
&lt;li data-end=&quot;282&quot; data-start=&quot;266&quot;&gt;@Transactional&lt;/li&gt;
&lt;li data-end=&quot;296&quot; data-start=&quot;283&quot;&gt;DI / IoC 이해&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;346&quot; data-start=&quot;298&quot; data-ke-size=&quot;size16&quot;&gt;=&amp;gt; *&amp;ldquo;자바는 할 줄 압니다&amp;rdquo;*는&lt;br /&gt;&lt;b&gt;스프링을 못 쓰면 의미가 거의 없습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;373&quot; data-start=&quot;353&quot; data-ke-size=&quot;size23&quot;&gt;2) 단순 문법 문제가 아님&lt;/h3&gt;
&lt;p data-end=&quot;399&quot; data-start=&quot;374&quot; data-ke-size=&quot;size16&quot;&gt;스프링을 못 쓴다는 건 보통 이런 상태입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;497&quot; data-start=&quot;401&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;437&quot; data-start=&quot;401&quot;&gt;@Service, @Repository 역할 구분 불가&lt;/li&gt;
&lt;li data-end=&quot;453&quot; data-start=&quot;438&quot;&gt;트랜잭션 경계 설정 못함&lt;/li&gt;
&lt;li data-end=&quot;467&quot; data-start=&quot;454&quot;&gt;DI 설계 감각 없음&lt;/li&gt;
&lt;li data-end=&quot;482&quot; data-start=&quot;468&quot;&gt;테스트 코드 작성 불가&lt;/li&gt;
&lt;li data-end=&quot;497&quot; data-start=&quot;483&quot;&gt;계층 분리 개념이 흐림&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;552&quot; data-start=&quot;499&quot; data-ke-size=&quot;size16&quot;&gt;이건 &lt;b&gt;툴 미숙&lt;/b&gt;이 아니라&lt;br /&gt;&lt;b&gt;=&amp;gt;서버 애플리케이션 설계 역량 부족&lt;/b&gt;으로 평가됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;592&quot; data-start=&quot;559&quot; data-ke-size=&quot;size23&quot;&gt;3)&amp;nbsp; &amp;ldquo;다른 프레임워크 하면 되지 않나요?&amp;rdquo;의 현실&lt;/h3&gt;
&lt;p data-end=&quot;615&quot; data-start=&quot;593&quot; data-ke-size=&quot;size16&quot;&gt;이론적으로는 가능하지만 현실은 다릅니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;대안현실
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;745&quot; data-start=&quot;617&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;745&quot; data-start=&quot;639&quot;&gt;
&lt;tr data-end=&quot;664&quot; data-start=&quot;639&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;652&quot; data-start=&quot;639&quot;&gt;순수 Servlet&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;664&quot; data-start=&quot;652&quot;&gt;실무 거의 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;691&quot; data-start=&quot;665&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;675&quot; data-start=&quot;665&quot;&gt;Node.js&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;691&quot; data-start=&quot;675&quot;&gt;Java 포지션과 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;726&quot; data-start=&quot;692&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;710&quot; data-start=&quot;692&quot;&gt;Python (Django)&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;726&quot; data-start=&quot;710&quot;&gt;주니어 진입 장벽 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;745&quot; data-start=&quot;727&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;732&quot; data-start=&quot;727&quot;&gt;Go&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;745&quot; data-start=&quot;732&quot;&gt;신입 거의 안 씀&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;785&quot; data-start=&quot;747&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;=&amp;gt; Java를 선택했다면 스프링은 선택이 아니라 필수&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;785&quot; data-start=&quot;747&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;785&quot; data-start=&quot;747&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;785&quot; data-start=&quot;747&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.스프링이 제공하는 최적화와 효율성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;214&quot; data-start=&quot;184&quot; data-ke-size=&quot;size26&quot;&gt;1) 객체 생성&amp;middot;관리 최적화 (IoC / DI)&lt;/h2&gt;
&lt;h3 data-end=&quot;233&quot; data-start=&quot;216&quot; data-ke-size=&quot;size23&quot;&gt;문제 (스프링 없을 때)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;282&quot; data-start=&quot;234&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;249&quot; data-start=&quot;234&quot;&gt;객체 생성 위치가 분산됨&lt;/li&gt;
&lt;li data-end=&quot;267&quot; data-start=&quot;250&quot;&gt;new 남발 &amp;rarr; 결합도 증가&lt;/li&gt;
&lt;li data-end=&quot;282&quot; data-start=&quot;268&quot;&gt;교체&amp;middot;테스트 비용 폭증&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;296&quot; data-start=&quot;284&quot; data-ke-size=&quot;size23&quot;&gt;스프링의 최적화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;356&quot; data-start=&quot;297&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;317&quot; data-start=&quot;297&quot;&gt;&lt;b&gt;싱글톤 컨테이너 기본 관리&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;335&quot; data-start=&quot;318&quot;&gt;객체 재사용 &amp;rarr; 메모리 절약&lt;/li&gt;
&lt;li data-end=&quot;356&quot; data-start=&quot;336&quot;&gt;의존성 변경 시 코드 수정 최소화&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766476071513&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service // 기본 싱글톤
public class OrderService { }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;=&amp;gt;객체 생성 비용 + 관리 비용 감소&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-end=&quot;471&quot; data-start=&quot;453&quot; data-ke-size=&quot;size26&quot;&gt;2) 트랜잭션 처리 최적화&lt;/h2&gt;
&lt;h3 data-end=&quot;479&quot; data-start=&quot;473&quot; data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;544&quot; data-start=&quot;480&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;515&quot; data-start=&quot;480&quot;&gt;try-catch + commit/rollback 직접 관리&lt;/li&gt;
&lt;li data-end=&quot;532&quot; data-start=&quot;516&quot;&gt;예외 누락 시 데이터 꼬임&lt;/li&gt;
&lt;li data-end=&quot;544&quot; data-start=&quot;533&quot;&gt;코드 가독성 최악&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;558&quot; data-start=&quot;546&quot; data-ke-size=&quot;size23&quot;&gt;스프링의 최적화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;626&quot; data-start=&quot;559&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;588&quot; data-start=&quot;559&quot;&gt;선언적 트랜잭션 (@Transactional)&lt;/li&gt;
&lt;li data-end=&quot;612&quot; data-start=&quot;589&quot;&gt;런타임 예외 기준 자동 rollback&lt;/li&gt;
&lt;li data-end=&quot;626&quot; data-start=&quot;613&quot;&gt;커넥션 효율적 재사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766476096205&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
public void saveOrder() { }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;=&amp;gt;정합성 유지 + 개발자 실수 제거&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-end=&quot;738&quot; data-start=&quot;716&quot; data-ke-size=&quot;size26&quot;&gt;3) AOP 기반 공통 로직 분리&lt;/h2&gt;
&lt;h3 data-end=&quot;746&quot; data-start=&quot;740&quot; data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;783&quot; data-start=&quot;747&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;768&quot; data-start=&quot;747&quot;&gt;로깅, 권한, 시간 측정 코드 중복&lt;/li&gt;
&lt;li data-end=&quot;783&quot; data-start=&quot;769&quot;&gt;핵심 로직 가독성 붕괴&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;797&quot; data-start=&quot;785&quot; data-ke-size=&quot;size23&quot;&gt;스프링의 최적화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;826&quot; data-start=&quot;798&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;810&quot; data-start=&quot;798&quot;&gt;프록시 기반 AOP&lt;/li&gt;
&lt;li data-end=&quot;826&quot; data-start=&quot;811&quot;&gt;핵심 비즈니스 로직 집중&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766476115812&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Around(&quot;execution(* service..*(..))&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;=&amp;gt;중복 제거 + 코드 응집도 상승&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-end=&quot;949&quot; data-start=&quot;911&quot; data-ke-size=&quot;size26&quot;&gt;4) 데이터 접근 성능 최적화 (Spring Data JPA)&lt;/h2&gt;
&lt;h3 data-end=&quot;964&quot; data-start=&quot;951&quot; data-ke-size=&quot;size23&quot;&gt;제공 최적화 요소&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1031&quot; data-start=&quot;965&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;983&quot; data-start=&quot;965&quot;&gt;1차 캐시 (영속성 컨텍스트)&lt;/li&gt;
&lt;li data-end=&quot;1006&quot; data-start=&quot;984&quot;&gt;쓰기 지연 (Batch Insert)&lt;/li&gt;
&lt;li data-end=&quot;1031&quot; data-start=&quot;1007&quot;&gt;변경 감지 (Dirty Checking)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766476135021&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;member.setName(&quot;변경&quot;);
// update 쿼리 자동 처리&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;=&amp;gt;불필요한 SQL 감소 = DB 부하 감소&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-end=&quot;1153&quot; data-start=&quot;1123&quot; data-ke-size=&quot;size26&quot;&gt;5) 웹 요청 처리 효율 (Spring MVC)&lt;/h2&gt;
&lt;h3 data-end=&quot;1166&quot; data-start=&quot;1155&quot; data-ke-size=&quot;size23&quot;&gt;최적화 포인트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1228&quot; data-start=&quot;1167&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1192&quot; data-start=&quot;1167&quot;&gt;DispatcherServlet 중앙 제어&lt;/li&gt;
&lt;li data-end=&quot;1213&quot; data-start=&quot;1193&quot;&gt;스레드 재사용 (서블릿 컨테이너)&lt;/li&gt;
&lt;li data-end=&quot;1228&quot; data-start=&quot;1214&quot;&gt;파라미터 바인딩 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766476154437&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@GetMapping(&quot;/users/{id}&quot;)
public UserDto get(@PathVariable Long id)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;=&amp;gt;요청 처리 비용 감소 + 코드 간결&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-end=&quot;1367&quot; data-start=&quot;1345&quot; data-ke-size=&quot;size26&quot;&gt;6) 예외 처리 &amp;amp; 안정성 최적화&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1427&quot; data-start=&quot;1369&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1401&quot; data-start=&quot;1369&quot;&gt;전역 예외 처리 (@ControllerAdvice)&lt;/li&gt;
&lt;li data-end=&quot;1413&quot; data-start=&quot;1402&quot;&gt;일관된 에러 응답&lt;/li&gt;
&lt;li data-end=&quot;1427&quot; data-start=&quot;1414&quot;&gt;장애 지점 추적 용이&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;=&amp;gt;운영 안정성 &amp;uarr; / 장애 대응 시간 &amp;darr;&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-end=&quot;1481&quot; data-start=&quot;1464&quot; data-ke-size=&quot;size26&quot;&gt;7) 테스트 효율 극대화&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1520&quot; data-start=&quot;1483&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1501&quot; data-start=&quot;1483&quot;&gt;DI 기반 Mock 주입 가능&lt;/li&gt;
&lt;li data-end=&quot;1520&quot; data-start=&quot;1502&quot;&gt;단위 테스트/통합 테스트 분리&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766476180455&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@MockBean
private UserRepository userRepository;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;=&amp;gt;테스트 작성 비용 감소 + 품질 상승&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 면접관이 해당 질문을 한다면 그 의도는.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;142&quot; data-start=&quot;109&quot; data-ke-size=&quot;size23&quot;&gt;1) &amp;ldquo;이 사람이 스프링을 &lt;b&gt;써본 사람&lt;/b&gt;인가?&amp;rdquo;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;258&quot; data-start=&quot;143&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;176&quot; data-start=&quot;143&quot;&gt;&lt;b&gt;의도&lt;/b&gt;: 공식 문서 암기 vs 실무 사용 경험 구분&lt;/li&gt;
&lt;li data-end=&quot;258&quot; data-start=&quot;177&quot;&gt;&lt;b&gt;보고 싶은 신호&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;258&quot; data-start=&quot;194&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;217&quot; data-start=&quot;194&quot;&gt;&amp;ldquo;CPU 최적화&amp;rdquo; 같은 추상적 얘기 X&lt;/li&gt;
&lt;li data-end=&quot;258&quot; data-start=&quot;220&quot;&gt;&lt;b&gt;트랜잭션, 객체 생명주기, 중복 제거&lt;/b&gt; 같은 현실적 얘기 O&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;321&quot; data-start=&quot;260&quot; data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 실무자는&lt;br /&gt;&amp;ldquo;스프링의 최적화는 성능보다 &lt;b&gt;실수 방지&amp;middot;유지보수 비용 감소&lt;/b&gt; 쪽이 큽니다&amp;rdquo;라고 답함&lt;/p&gt;
&lt;p data-end=&quot;321&quot; data-start=&quot;260&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;365&quot; data-start=&quot;328&quot; data-ke-size=&quot;size23&quot;&gt;2) &amp;ldquo;프레임워크의 역할을 &lt;b&gt;과대평가&lt;/b&gt;하지는 않는가?&amp;rdquo;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;470&quot; data-start=&quot;366&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;392&quot; data-start=&quot;366&quot;&gt;&lt;b&gt;의도&lt;/b&gt;: 스프링을 만능으로 보는지 확인&lt;/li&gt;
&lt;li data-end=&quot;470&quot; data-start=&quot;393&quot;&gt;&lt;b&gt;보고 싶은 신호&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;470&quot; data-start=&quot;410&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;435&quot; data-start=&quot;410&quot;&gt;&amp;ldquo;스프링이 알아서 성능 다 잡아줍니다&amp;rdquo; X&lt;/li&gt;
&lt;li data-end=&quot;470&quot; data-start=&quot;438&quot;&gt;&amp;ldquo;설계를 단순화해 &lt;b&gt;잘못된 최적화를 줄여준다&lt;/b&gt;&amp;rdquo; O&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;519&quot; data-start=&quot;472&quot; data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 면접관은&lt;br /&gt;**&amp;lsquo;스프링이 뭘 해주고, 뭘 안 해주는지&amp;rsquo;**를 아는지를 봅니다.&lt;/p&gt;
&lt;p data-end=&quot;519&quot; data-start=&quot;472&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;519&quot; data-start=&quot;472&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;519&quot; data-start=&quot;472&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;564&quot; data-start=&quot;526&quot; data-ke-size=&quot;size23&quot;&gt;3) &amp;ldquo;이 사람이 &lt;b&gt;왜 이 구조를 쓰는지&lt;/b&gt; 알고 있나?&amp;rdquo;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;670&quot; data-start=&quot;565&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;588&quot; data-start=&quot;565&quot;&gt;&lt;b&gt;의도&lt;/b&gt;: 관성적인 사용자인지 판단&lt;/li&gt;
&lt;li data-end=&quot;670&quot; data-start=&quot;589&quot;&gt;&lt;b&gt;보고 싶은 신호&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;670&quot; data-start=&quot;606&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;619&quot; data-start=&quot;606&quot;&gt;DI &amp;rarr; 결합도 감소&lt;/li&gt;
&lt;li data-end=&quot;638&quot; data-start=&quot;622&quot;&gt;AOP &amp;rarr; 공통 로직 분리&lt;/li&gt;
&lt;li data-end=&quot;670&quot; data-start=&quot;641&quot;&gt;@Transactional &amp;rarr; 정합성&amp;middot;커넥션 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;713&quot; data-start=&quot;672&quot; data-ke-size=&quot;size16&quot;&gt;=&amp;gt; &amp;ldquo;왜 쓰는지&amp;rdquo; 설명 못 하면&lt;br /&gt;&lt;b&gt;복붙 개발자&lt;/b&gt;로 분류됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;755&quot; data-start=&quot;720&quot; data-ke-size=&quot;size23&quot;&gt;4) &amp;ldquo;문제 생겼을 때 &lt;b&gt;원인 추적&lt;/b&gt;이 가능한가?&amp;rdquo;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;854&quot; data-start=&quot;756&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;778&quot; data-start=&quot;756&quot;&gt;&lt;b&gt;의도&lt;/b&gt;: 장애 대응 가능성 평가&lt;/li&gt;
&lt;li data-end=&quot;854&quot; data-start=&quot;779&quot;&gt;&lt;b&gt;보고 싶은 신호&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;854&quot; data-start=&quot;796&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;816&quot; data-start=&quot;796&quot;&gt;프록시/AOP/트랜잭션 경계 이해&lt;/li&gt;
&lt;li data-end=&quot;854&quot; data-start=&quot;819&quot;&gt;&amp;ldquo;이 지점에서 프록시 때문에 동작이 달라질 수 있다&amp;rdquo;는 인식&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;905&quot; data-start=&quot;856&quot; data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 이 질문은&lt;br /&gt;**&amp;lsquo;운영 중 문제를 혼자 해결할 수 있나?&amp;rsquo;**를 보는 질문입니다.&lt;/p&gt;
&lt;p data-end=&quot;905&quot; data-start=&quot;856&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;905&quot; data-start=&quot;856&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;940&quot; data-start=&quot;912&quot; data-ke-size=&quot;size23&quot;&gt;5) &amp;ldquo;우리 팀에 바로 투입해도 되는가?&amp;rdquo;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1031&quot; data-start=&quot;941&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;960&quot; data-start=&quot;941&quot;&gt;&lt;b&gt;의도&lt;/b&gt;: 온보딩 비용 계산&lt;/li&gt;
&lt;li data-end=&quot;1031&quot; data-start=&quot;961&quot;&gt;&lt;b&gt;합격 신호&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1031&quot; data-start=&quot;975&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1010&quot; data-start=&quot;975&quot;&gt;스프링이 &lt;b&gt;개발 속도&amp;middot;안정성&amp;middot;협업 효율&lt;/b&gt;을 높인다는 설명&lt;/li&gt;
&lt;li data-end=&quot;1031&quot; data-start=&quot;1013&quot;&gt;코드 품질 관점의 최적화 언급&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daily Dev Q&amp;amp;A</category>
      <author>awake123</author>
      <guid isPermaLink="true">https://awake123.tistory.com/41</guid>
      <comments>https://awake123.tistory.com/41#entry41comment</comments>
      <pubDate>Mon, 22 Dec 2025 18:18:16 +0900</pubDate>
    </item>
    <item>
      <title>전원희씨 rest api 피드백</title>
      <link>https://awake123.tistory.com/40</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발자 : 전원희&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 : ReDeal - 중고 거래 플랫폼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 로그아웃시 알림 창이 다수 발생.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-&amp;gt; 시연 도중 발생한거라 캡쳐는 없지만 다수 발생하는것을 확인 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 엔티티 사용시 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;createdDate과 updatedDate는&amp;nbsp;공통 엔티티로 뺄수 있을것 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;301&quot; data-origin-height=&quot;101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/55Gyx/dJMcabpfiKI/iybBr6b7MYCE3krVLAFCPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/55Gyx/dJMcabpfiKI/iybBr6b7MYCE3krVLAFCPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/55Gyx/dJMcabpfiKI/iybBr6b7MYCE3krVLAFCPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F55Gyx%2FdJMcabpfiKI%2FiybBr6b7MYCE3krVLAFCPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;301&quot; height=&quot;101&quot; data-origin-width=&quot;301&quot; data-origin-height=&quot;101&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgMYgk/dJMcafkS38s/irtKqtyV2a1Hz7snKf5VM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgMYgk/dJMcafkS38s/irtKqtyV2a1Hz7snKf5VM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgMYgk/dJMcafkS38s/irtKqtyV2a1Hz7snKf5VM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgMYgk%2FdJMcafkS38s%2FirtKqtyV2a1Hz7snKf5VM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;558&quot; height=&quot;232&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;427&quot; data-origin-height=&quot;147&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rmErD/dJMcahCZUWA/PixJJwjUOyTxoicSSbDWSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rmErD/dJMcahCZUWA/PixJJwjUOyTxoicSSbDWSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rmErD/dJMcahCZUWA/PixJJwjUOyTxoicSSbDWSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrmErD%2FdJMcahCZUWA%2FPixJJwjUOyTxoicSSbDWSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;427&quot; height=&quot;147&quot; data-origin-width=&quot;427&quot; data-origin-height=&quot;147&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. status 사용시 string보다 enum을 사용하는게 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BeGgz/dJMcajgtfnv/PK8omF3KZ5oyEiFjTIaEBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BeGgz/dJMcajgtfnv/PK8omF3KZ5oyEiFjTIaEBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BeGgz/dJMcajgtfnv/PK8omF3KZ5oyEiFjTIaEBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBeGgz%2FdJMcajgtfnv%2FPK8omF3KZ5oyEiFjTIaEBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;477&quot; height=&quot;101&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;101&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 추가적으로 status는 &quot;판매중&quot; , &quot;완료&quot; 같은 한글보다 영어나 코드로 저장하는것이 더 나을것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 삭제시 이미 한번 조회를 해온 후라 굳이 ID기반으로 삭제할 필요는 없을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;325&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BxSZb/dJMcad1CyMg/cq2REVIVLioomx7PGLRFlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BxSZb/dJMcad1CyMg/cq2REVIVLioomx7PGLRFlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BxSZb/dJMcad1CyMg/cq2REVIVLioomx7PGLRFlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBxSZb%2FdJMcad1CyMg%2Fcq2REVIVLioomx7PGLRFlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;842&quot; height=&quot;325&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;325&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의문점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.로그인 / 회원가입시 미리 데이터가 입력되어있는 부분들이 있었는데 왜 그런건지?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 현재 git 상 엔티티에 OneToMany나 ManyToOne등의 연관관계 매핑이 안보이는데 의도된 사안인지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로젝트</category>
      <author>awake123</author>
      <guid isPermaLink="true">https://awake123.tistory.com/40</guid>
      <comments>https://awake123.tistory.com/40#entry40comment</comments>
      <pubDate>Mon, 22 Dec 2025 15:05:24 +0900</pubDate>
    </item>
  </channel>
</rss>