<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>soidev 님의 블로그</title>
    <link>https://soidev.tistory.com/</link>
    <description>soidev 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Fri, 22 May 2026 04:28:20 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>soidev</managingEditor>
    <image>
      <title>soidev 님의 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/7549574/attach/7a3e2b2286834bd1b8b333d19d39baec</url>
      <link>https://soidev.tistory.com</link>
    </image>
    <item>
      <title>[DB 성능 최적화] | 인덱스부터 파티셔닝, 샤딩까지</title>
      <link>https://soidev.tistory.com/45</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 면접을 많이 봤는데 대규모 트래픽시 어떻게 해야할건지 라는 질문을 많이 받았다&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-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4&quot;&gt;1. 데이터가 많아지면 발생하는 문제&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;백엔드 개발을 하다 보면 처음엔 빨랐던 서비스가 데이터가 쌓일수록 느려지는 걸 경험하게 된다. 작년에 진행한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b data-index-in-node=&quot;60&quot; data-path-to-node=&quot;5&quot;&gt;상담 예약 시스템&lt;/b&gt;처럼 예약 기록이나 AI 리뷰 요약 데이터가 조 단위로 쌓인다면, 단순한 쿼리로는 한계가 온다. 이때 효과적으로 데이터를 관리하기 위해 인덱스, 파티셔닝, 샤딩 같은 전략이 필요하다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;6&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6&quot;&gt;2. 인덱스(Index) 이해하기&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;인덱스는 책의 맨 뒤에 있는 **'색인'**과 같다. 테이블 전체를 다 뒤지지 않고도 원하는 정보가 있는 위치로 바로 점프하는 기술이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;원리:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;B-Tree 구조를 사용해 검색 속도를 높인다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;장점:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;특정 상담사 예약 현황을 조회하거나 날짜별로 정렬할 때 성능이 비약적으로 좋아진다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;주의점:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;데이터가 추가되거나 수정될 때마다 인덱스도 갱신해야 하므로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b data-index-in-node=&quot;38&quot; data-path-to-node=&quot;8,2,0&quot;&gt;쓰기 성능&lt;/b&gt;은 조금 떨어진다. 꼭 필요한 컬럼에만 전략적으로 걸어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;9&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9&quot;&gt;3. 파티셔닝(Partitioning)으로 쪼개기&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;인덱스만으로 감당이 안 될 정도로 테이블이 무거워지면, 테이블을 물리적으로 나누는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b data-index-in-node=&quot;46&quot; data-path-to-node=&quot;10&quot;&gt;파티셔닝&lt;/b&gt;을 고려해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;방식:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;보통 '연도별'이나 '월별'로 데이터를 나눈다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;핵심 (Partition Pruning):&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;2026년 3월 예약&quot;을 조회하면 DB는 2024, 2025년 데이터 파티션은 아예 무시하고 해당 파티션만 뒤진다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,2,0&quot;&gt;장점:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;오래된 상담 이력을 백업하거나 삭제할 때,&lt;span&gt;&amp;nbsp;&lt;/span&gt;DELETE&lt;span&gt;&amp;nbsp;&lt;/span&gt;문 대신 파티션 자체를 날려버리면 되기 때문에 부하가 거의 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;12&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12&quot;&gt;4. 샤딩(Sharding)으로 서버 분산하기&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;파티셔닝이 한 컴퓨터 안에서 구역을 나누는 거라면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b data-index-in-node=&quot;29&quot; data-path-to-node=&quot;13&quot;&gt;샤딩&lt;/b&gt;은 데이터를 여러 대의 서버에 나눠 담는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b data-index-in-node=&quot;54&quot; data-path-to-node=&quot;13&quot;&gt;수평 확장(Scale-out)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;전략이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-path-to-node=&quot;14&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,0,0&quot;&gt;원리:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;'샤드 키(Shard Key)'를 기준으로 데이터를 분산한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예시: 서울 지역 상담 데이터는 A 서버, 부산 지역은 B 서버에 저장.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,0&quot;&gt;장점:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;단일 서버의 하드웨어 한계를 넘어서서 이론적으로 무한한 확장이 가능하다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,2,0&quot;&gt;단점:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;서버가 여러 대라 데이터를 한꺼번에 조인(Join)하기 어렵고 시스템 복잡도가 올라간다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;15&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;16&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16&quot;&gt;5. DB 최적화 구조도&lt;/b&gt;&lt;/h3&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwiusOKSu7qTAxUAAAAAHQAAAAAQtQI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;&lt;span&gt;Plaintext&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;[사용자 요청] (예: 특정 상담사 예약 조회)
        │
        ▼
   ┌─────────────┐
   │  Query Optimizer │  &amp;larr; 인덱스가 있는지 확인
   └─────────────┘
        │
        ├─ [Index 활용] ──▶ B-Tree 탐색 후 데이터 즉시 반환
        │
        └─ [데이터가 너무 많다면?]
                │
                ▼
   ┌──────────────────────────┐
   │      Partitioning        │  &amp;larr; 테이블을 날짜별로 분할
   │ (2025 파티션 | 2026 파티션)  │
   └──────────────────────────┘
                │
                ▼
   ┌────────────────────────────────┐
   │            Sharding            │  &amp;larr; 서버 자체를 여러 대로 분산
   │ [Shard 1 (서울)]  [Shard 2 (부산)] │
   └────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;18&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18&quot;&gt;6. 요약 및 결론&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;무조건 최신 기술을 쓰는 게 답은 아니다. 서비스의 규모에 맞춰서 단계적으로 접근해야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-path-to-node=&quot;20&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,0,0&quot;&gt;Index:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;조회 조건이 명확하고 특정 컬럼 검색이 잦을 때 기본으로 설정.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,1,0&quot;&gt;Partitioning:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;대규모 예약 이력처럼 시간순 데이터 관리가 힘들 때 도입.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,2,0&quot;&gt;Sharding:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;서비스가 전국 단위로 커져서 단일 서버 리소스가 부족할 때 최종 단계로 고려.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;21&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;21&quot;&gt;결론:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;예약 시스템처럼 데이터 정합성과 속도가 모두 중요한 도메인에서는 이 세 가지를 적재적소에 배치하는 아키텍처 설계 능력이 중요하다.&lt;/p&gt;</description>
      <category>중앙정보기술인재개발원/DB</category>
      <author>soidev</author>
      <guid isPermaLink="true">https://soidev.tistory.com/45</guid>
      <comments>https://soidev.tistory.com/45#entry45comment</comments>
      <pubDate>Thu, 26 Mar 2026 15:06:34 +0900</pubDate>
    </item>
    <item>
      <title>[개인 서버 운영] | 라즈베리파이 이해하기</title>
      <link>https://soidev.tistory.com/43</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1. 라즈베리파이(Raspberry Pi)란?&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;라즈베리파이는&amp;nbsp;&lt;b&gt;신용카드 크기의 작은 컴퓨터&lt;/b&gt;지만, 일반 PC처럼 운영체제를 설치하고 프로그램을 실행할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;저렴한 가격과 작은 크기 때문에 개인 서버, IoT 기기, 홈서버 등 다양한 용도로 활용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉, 집에서도 라즈베리파이를 서버처럼 켜두고, 외부에서 접속해 서비스를 운영할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;2. 라즈베리파이를 서버로 사용하기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;라즈베리파이를 서버로 활용한다는 것은, 다른 컴퓨터나 스마트폰 등에서 접속해서 정보를 주고받는 역할을 맡긴다는 뜻이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버 역할을 하는 라즈베리파이는&amp;nbsp;&lt;b&gt;항상 켜져 있어야&lt;/b&gt;&amp;nbsp;하며, 외부에서 접근 가능한 환경을 만들어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그럼 외부에서 어떻게 접속할 수 있는지 네트워크 구조를 이해해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;3. 내부망과 외부망 이해하기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&amp;nbsp; 3-1. 내부망(Private Network)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;집이나 사무실에서 여러 장치가 연결되는 네트워크&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;내부 장치끼리는 서로 접속이 가능하지만, 외부 인터넷에서는 바로 접근할 수 없다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예시:&amp;nbsp;172.30.1.37&amp;nbsp;같은 IP&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ipconfig&amp;nbsp;명령어를 통해 확인할 수 있는 IP가 바로 내부망 IP다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&amp;nbsp; 3-2. 공유기와 ISP&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;외부 인터넷과 연결되는 장치는 **공유기(Gateway)**를 통해 연결된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ISP(예: KT, SKT, LG)는 공유기에&amp;nbsp;&lt;b&gt;Public IP&lt;/b&gt;를 부여한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;공유기는 내부 장치들에게 Private IP를 할당하고, 외부 요청은 포트포워딩을 통해 특정 장치로 전달한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;내부망 IP는 다른 집에서도 똑같이 쓸 수 있지만, 공유기 뒤에 있기 때문에 충돌이 없다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&amp;nbsp; 3-3. 기지국 = 거대한 공유기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;우리가 흔히 말하는 기지국은 통신사에서 여러 공유기를 관리하는 장치다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기지국을 통해 다른 공유기나 장치와 통신이 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;4. 라즈베리파이를 외부에서 접속하기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;집 안에서 라즈베리파이를 켰다고 해서 외부에서 바로 접속할 수 있는 것은 아니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;외부 접속을 위해서는&amp;nbsp;&lt;b&gt;포트포워딩&lt;/b&gt;과&amp;nbsp;&lt;b&gt;DDNS&lt;/b&gt;&amp;nbsp;설정이 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;4-1. 포트포워딩(Port Forwarding)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;공유기는 내부망과 외부망을 구분하기 때문에, 외부에서 들어오는 요청이 라즈베리파이까지 도달하지 못한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;포트포워딩은&amp;nbsp;&lt;b&gt;외부에서 들어오는 특정 포트를 라즈베리파이 내부 IP와 포트로 연결&lt;/b&gt;해주는 기능이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예시: 외부에서&amp;nbsp;5022번 포트로 접속 &amp;rarr; 공유기가 이를 라즈베리파이 SSH 포트로 전달 &amp;rarr; 접속 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;4-2. 도메인 연결과 유동 IP&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;대부분 가정용 인터넷은&amp;nbsp;&lt;b&gt;유동 IP&lt;/b&gt;를 사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉, 집의 공인 IP는 시간이 지나면 바뀌므로 도메인 연결만으로는 접속 불가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해결책:&amp;nbsp;&lt;b&gt;DDNS(Dynamic Domain Name Service)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스크립트나 프로그램으로 5분마다 현재 Public IP를 확인&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;도메인에 IP를 자동으로 업데이트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이렇게 하면 유동 IP 환경에서도 도메인으로 라즈베리파이에 접속 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단점: IP 변경 시 평균 2~3분 정도 접속 불가 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;5. 웹서버 포트와 HTTPS&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기본 HTTP 포트:&amp;nbsp;&lt;b&gt;80번&lt;/b&gt;, HTTPS 포트:&amp;nbsp;&lt;b&gt;443번&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;포트포워딩을 설정할 때, 서버가 사용하는 포트와 공유기의 포트가 일치해야 외부에서 정상 접속 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;포트 충돌이나 설정 오류 시, 웹서버가 돌아도 접속이 되지 않거나 HTTP만 가능할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;필요하면 HTTPS를 위해 인증서 설치 후 443번 포트를 연결해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1759142871180&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[외부 인터넷] (도메인: www.내도메인.com)
        │
        │  요청
        ▼
   ┌─────────────┐
   │   DNS 서버   │  &amp;larr; 도메인 요청 시 현재 Public IP 확인
   └─────────────┘
        │
        │
        ▼
   ┌─────────────┐
   │   ISP 기지국  │  &amp;larr; 통신사에서 제공하는 Public IP
   └─────────────┘
        │
        │
        ▼
   ┌─────────────┐
   │ 공유기(Gateway) │  &amp;larr; 포트포워딩 설정 필요
   │ Public IP     │
   │ (유동 IP)     │
   └─────────────┘
        │
        │  포트포워딩: 외부 5022 &amp;rarr; 라즈베리파이 22
        ▼
   ┌─────────────┐
   │ 라즈베리파이    │  &amp;larr; 내부망 IP: 172.30.1.37
   │ 서버 역할      │
   │ SSH/웹서버 실행 │
   └─────────────┘
        │
        ▼
   [내부 장치들]  &amp;larr; 라즈베리파이와 같은 내부망 장치들은 직접 접속 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;구조 설명&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;외부 인터넷에서 도메인으로 접속하면,&amp;nbsp;&lt;b&gt;ISP 기지국 &amp;rarr; 공유기&lt;/b&gt;&amp;nbsp;순서로 요청이 전달된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;공유기에서&amp;nbsp;&lt;b&gt;포트포워딩&lt;/b&gt;&amp;nbsp;설정을 통해 요청을 라즈베리파이로 보낸다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;라즈베리파이는 내부망 IP를 가지고 있으며, SSH나 웹서버 등 요청을 처리한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;내부 장치끼리는 공유기 없이도 서로 접속 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>IP</category>
      <category>개발자</category>
      <category>네트워크</category>
      <category>도메인</category>
      <category>라즈베리파이</category>
      <category>서버</category>
      <author>soidev</author>
      <guid isPermaLink="true">https://soidev.tistory.com/43</guid>
      <comments>https://soidev.tistory.com/43#entry43comment</comments>
      <pubDate>Mon, 29 Sep 2025 21:02:29 +0900</pubDate>
    </item>
    <item>
      <title>[React] 게시판 만들기_1</title>
      <link>https://soidev.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;backend : spring&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;frontend : react&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;package.json &amp;gt; dependencies &amp;nbsp;: 18.3.1버전&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링에서 build.gradle이거랑 똑같은 파일이라고 생각하면됨&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;package-lock.json react를 의존하는 라이브러리를 연쇄적으로 설치함&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;npm install&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;npm install react-router-dom&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;npm install axios&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;npm run dev&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;public,src,index.html,package.json 만 들고가면 됨 깃이나 다른데서 이 소스를 하고싶을때&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;public 쪽은 기본 이미지 들어감. spring에서 static에있던것들을 여기선 public에다가 넣음&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;src &amp;gt; jsx,component,service&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;페이지별로 폴더를만들고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;얘에 있는 공용을 만들기??&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x8ajc/btsPlxGEmli/zWtKp9kGN6kSrQIhK6Y6oK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x8ajc/btsPlxGEmli/zWtKp9kGN6kSrQIhK6Y6oK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x8ajc/btsPlxGEmli/zWtKp9kGN6kSrQIhK6Y6oK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx8ajc%2FbtsPlxGEmli%2FzWtKp9kGN6kSrQIhK6Y6oK%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;188&quot; height=&quot;282&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mo8dj/btsPltdcpFL/4gzANvD3dRK2otqA4VgWKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mo8dj/btsPltdcpFL/4gzANvD3dRK2otqA4VgWKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mo8dj/btsPltdcpFL/4gzANvD3dRK2otqA4VgWKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmo8dj%2FbtsPltdcpFL%2F4gzANvD3dRK2otqA4VgWKk%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;199&quot; height=&quot;299&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;660&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;파일구조는 파일마다 컴포넌트 하기 이런식으로&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;링크. : 액터/기능/페이지 user/board/loginPage&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;라우터는 언제든지 할 수 있는거?!?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1752643570985&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState } from &quot;react&quot;;
import BottomNavigation from &quot;../../commons/component/Bottomnavigation&quot;;

export default function RegisterPage() {

    //상태변수를 만든다.
    const [accountName,setAccountName] = useState('123123');

    return (
        &amp;lt;&amp;gt;
            &amp;lt;div className=&quot;container-fluid&quot;&amp;gt;
                &amp;lt;div className=&quot;row&quot;&amp;gt;
                    &amp;lt;div className=&quot;col&quot;&amp;gt;
                        회원 가입&amp;lt;br/&amp;gt;
                        아이디: &amp;lt;input value={accountName} type=&quot;text&quot; placeholder=&quot;아이디&quot; /&amp;gt;&amp;lt;br/&amp;gt;
                        비번: &amp;lt;input type=&quot;password&quot; placeholder=&quot;비번&quot; /&amp;gt;&amp;lt;br/&amp;gt;
                        닉넴: &amp;lt;input type=&quot;text&quot; placeholder=&quot;닉네임&quot; /&amp;gt;&amp;lt;br/&amp;gt;
                        성별 : &amp;lt;input type=&quot;radio&quot; name=&quot;gender&quot; /&amp;gt;남
                        &amp;lt;input type=&quot;radio&quot; name=&quot;gender&quot; /&amp;gt;여&amp;lt;br/&amp;gt;
                        생일: &amp;lt;input type=&quot;date&quot; placeholder=&quot;생년월일&quot; /&amp;gt;&amp;lt;br/&amp;gt;
                        이메일: &amp;lt;input type=&quot;text&quot; placeholder=&quot;이메일&quot; /&amp;gt;&amp;lt;br/&amp;gt;
                        폰넘버: &amp;lt;input type=&quot;text&quot; placeholder=&quot;폰넘버&quot; /&amp;gt;&amp;lt;br/&amp;gt;
                        &amp;lt;button className=&quot;btn btn-primary&quot;&amp;gt;회원 가입&amp;lt;/button&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;

            &amp;lt;BottomNavigation /&amp;gt;
        &amp;lt;/&amp;gt;
    );
}&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;글자를 쓸 때마다 onchange가 발생함 그래서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z2Z1H/btsPksfT48J/CQv2de5lvsLk2dYmFAIfAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z2Z1H/btsPksfT48J/CQv2de5lvsLk2dYmFAIfAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z2Z1H/btsPksfT48J/CQv2de5lvsLk2dYmFAIfAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz2Z1H%2FbtsPksfT48J%2FCQv2de5lvsLk2dYmFAIfAK%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;398&quot; height=&quot;367&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;123123 부분에 안 고쳐지고 안 바뀐다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752643738683&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const handleAccountNameChange = (e) =&amp;gt; {
        setAccountName(e.target.value);
    };
    
    .....
    
    
 아이디: &amp;lt;input onChange={handleAccountNameChange} value={accountName} type=&quot;text&quot; placeholder=&quot;아이디&quot; /&amp;gt;&amp;lt;br/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이런식으로 이벤트를 가져오면 123123부분이 수정되면서 알아서 랜더링 된다!&lt;/span&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;하지만 지금 input이 10개 정도 있는데 state랑 eventhandle를 10개씩 만들기는 너무 귀찮고 하니 다른 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1752711509882&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState } from &quot;react&quot;;
import BottomNavigation from &quot;../../commons/component/Bottomnavigation&quot;;
import axios from &quot;axios&quot;;
import { useNavigate } from &quot;react-router-dom&quot;;

export default function RegisterPage() {
    
    //state입력양식에 엮는것 젤 중요함
    const [formData, setFormData] = useState({
        accountName: '',
        password: '',
        nickname: '',
        email: '',
        gender: '',
        birth: '',
        phone: ''
    });

    // console.log(formData);
    
    const handleChange = (e) =&amp;gt; {
        //굉장히 중요한 문법
        //변수선언해서 바로 값을 넘어주는 문법
        //구조분해문법
        //type,checked는 체크박스용임 그냥 쓰샤!
        const { name, value,type,checked } = e.target;
        
        const newValue = type === 'checkbox' ? checked : value;     //체크 박스 없어도 이거 써도 작동되나요? 네 됩니당 그냥 쓰세요 달력도 value임
        // 체크 박스일땐 value가 true false로 넘어옴 하지만 지금 라디오 버튼이라 value가 문자로 넘어옴


        //... : 원래값을 그대로 쓰겠다는 것
        //name은 accountName, value는 입력한 값
        //setFormData는 상태변수의 setter함수
        //formData는 상태변수의 현재값
        //이렇게 하면 formData의 값이 바뀌면서 랜더링이 다시 이루어지면서 화면에 입력한 값이 보임 이렇게 하면 상태변수의 값을 바꿀 수 있다.
        setFormData({
            ...formData,
            [name]: value
        });
    }

    //post같은 거 쓸 땐  axios이런거 쓸 때 async() 활용
    const navigate = useNavigate(); //href 하면 웹브라우저가 request해서 웹브라우저가 깜박임 근ㄷ게 react에선 무조건 깜박임 안됨 그래서 navi로 활용

    const handleSubmit = async()=&amp;gt;{
        //http://localhost:8080/api/user/register post로 axios로 전송
        const response = await axios.post('http://localhost:8080/api/user/register',formData);
        //.data =json

        //  여러 예외 처리 해야될 수 도 있음.

        alert(&quot;회원가입이 완료 되었습니다.&quot;);   //이거말고 다른 디자인으로 활용 가능

        //성공시 로그인 페이지로 이동
        navigate('/user/board/login');
    }


    return (
        &amp;lt;&amp;gt;
            &amp;lt;div className=&quot;container-fluid&quot;&amp;gt;
                &amp;lt;div className=&quot;row&quot;&amp;gt;
                    &amp;lt;div className=&quot;col&quot;&amp;gt;
                        회원 가입&amp;lt;br/&amp;gt;
                        아이디: &amp;lt;input onChange={handleChange} name=&quot;accountName&quot; value={formData.accountName} type=&quot;text&quot; placeholder=&quot;아이디&quot; /&amp;gt;&amp;lt;br/&amp;gt;
                        비번: &amp;lt;input onChange={handleChange} name=&quot;password&quot; value={formData.password} type=&quot;password&quot; placeholder=&quot;비번&quot; /&amp;gt;&amp;lt;br/&amp;gt;
                        닉넴: &amp;lt;input onChange={handleChange} name=&quot;nickname&quot; value={formData.nickname} type=&quot;text&quot; placeholder=&quot;닉네임&quot; /&amp;gt;&amp;lt;br/&amp;gt;
                        {/* 처음부터 초기값을 주면 된다??? */}
                        성별 : &amp;lt;input onChange={handleChange} name=&quot;gender&quot; checked={formData.gender=='M'} value='M'  type=&quot;radio&quot; /&amp;gt;남
                        &amp;lt;input onChange={handleChange} name=&quot;gender&quot; checked={formData.gender=='F'} value='F' type=&quot;radio&quot; /&amp;gt;여&amp;lt;br/&amp;gt;
                        생일: &amp;lt;input onChange={handleChange} name=&quot;birth&quot; value={formData.birth} type=&quot;date&quot; placeholder=&quot;생년월일&quot; /&amp;gt;&amp;lt;br/&amp;gt;
                        이메일: &amp;lt;input onChange={handleChange} name=&quot;email&quot; value={formData.email} type=&quot;text&quot; placeholder=&quot;이메일&quot; /&amp;gt;&amp;lt;br/&amp;gt;
                        폰넘버: &amp;lt;input onChange={handleChange} name=&quot;phone&quot; value={formData.phone} type=&quot;text&quot; placeholder=&quot;폰넘버&quot; /&amp;gt;&amp;lt;br/&amp;gt;
                        &amp;lt;button onClick={handleSubmit} className=&quot;btn btn-primary&quot;&amp;gt;회원 가입&amp;lt;/button&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;

            &amp;lt;BottomNavigation /&amp;gt;
        &amp;lt;/&amp;gt;
    );
}&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;axios : &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Axios는 브라우저, Node.js를 위한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Promise API를 활용하는 HTTP 비동기 통신 라이브러리이다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;운영 환경에 따라 브라우저의 XMLHttpRequest 객체 또는 Node.js의 HTTP API 사용&lt;/li&gt;
&lt;li&gt;Promise(ES6) API 사용&lt;/li&gt;
&lt;li&gt;요청과 응답 데이터의 변형&lt;/li&gt;
&lt;li&gt;HTTP 요청 취소 및 요청과 응답을 JSON 형태로 자동 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;-http-methods&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;# HTTP Methods&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클라이언트가 웹서버에게 사용자 요청의 목적/종류를 알리는 수단&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 Method중 Axios 통신하면서 가장 많이 사용되는 메소드를 정리해보았다.&lt;/p&gt;
&lt;h3 id=&quot;1-get&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1. GET&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET : 입력한 url에 존재하는 자원에 요청을 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;문법&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;문법&lt;/h4&gt;
&lt;pre class=&quot;arduino&quot; style=&quot;background-color: #fbfcfd; color: #24292e; text-align: left;&quot;&gt;&lt;code&gt;axios.get(url,[,config])&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Q) Get이 데이터를 받아오는 것이라고 했는데, 저는 로그인을 구현할때 GET을 사용했는데요?&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GET으로 로그인을 구현했을때 웹 사이트 주소창의 형태를 잘 보면 이러한 형태가 나온다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #fbfcfd; color: #24292e; text-align: left;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;www.server.com/login?accountName=t0001&amp;amp;pw=1111  // 실제로 없는 사이트&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웹 사이트 뒤에 쿼리스트링이 붙여진 것을 확인할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;✅ GET은 서버에서 어떤 데이터를 가져와서 보여준다거나 하는 용도이다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-post&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2. POST&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POST : 새로운 리소스를 생성(create)할 때 사용한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;문법-1&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;문법&lt;/h4&gt;
&lt;pre class=&quot;haskell&quot; style=&quot;background-color: #fbfcfd; color: #24292e; text-align: left;&quot;&gt;&lt;code&gt;axios.post(&quot;url주소&quot;,{
  data객체
    },[,config])&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;POST 메서드의 두 번째 인자는 본문으로 보낼 데이터를 설정한 객체 리터럴을 전달한다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Q) Post는 새로운 리소스를 생성할 때 사용되는데 그러면 언제 POST를 사용하나요?&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; 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 id=&quot;3-delete&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3. Delete&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST 기반 API 프로그램에서 데이터베이스에 저장되어 있는 내용을 삭제하는 목적으로 사용한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;문법-2&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;문법&lt;/h4&gt;
&lt;pre class=&quot;arduino&quot; style=&quot;background-color: #fbfcfd; color: #24292e; text-align: left;&quot;&gt;&lt;code&gt;axios.delete(url,[,config]);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;✅ Delete메서드는 HTML Form 태그에서 기본적으로 지원하는 HTTP 메서드가 아니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Delete메서드는 서버에 있는 데이터베이스의 내용을 삭제하는 것을 주 목적으로 하기에 두 번째 인자를 아예 전달하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;4-put&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4. PUT&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST 기반 API 프로그램에서 데이터베이스에 저장되어 있는 내용을 갱신하는 목적으로 사용된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;문법-3&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;문법&lt;/h4&gt;
&lt;pre class=&quot;arduino&quot; style=&quot;background-color: #fbfcfd; color: #24292e; text-align: left;&quot;&gt;&lt;code&gt;axios.put(url[, data[, config]])&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;✅ PUT메서드는 HTML Form 태그에서 기본적으로 지원하는 HTTP 메서드가 아니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PUT메서드는 서버에 있는 데이터베이스의 내용을 변경하는 것을 주 목적으로 하고 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; 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;이제 이 회원가입 프론트앤드 코드를 마무리하면 백엔드로 보내야하니까 axios로 보냈지만&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNdfyN/btsPkRGyL79/VJt4KEo7j3uWdyfNR5hkW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNdfyN/btsPkRGyL79/VJt4KEo7j3uWdyfNR5hkW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNdfyN/btsPkRGyL79/VJt4KEo7j3uWdyfNR5hkW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNdfyN%2FbtsPkRGyL79%2FVJt4KEo7j3uWdyfNR5hkW1%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;1670&quot; height=&quot;486&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cors 오류가 떴음 &amp;nbsp;이유가 뭐냐 백엔드는 8080 프론트는 5173 서로 서버 주소가 다르니까 백쪽에서 코드를 건드려야함&lt;/p&gt;
&lt;pre id=&quot;code_1752648652797&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping(&quot;/**&quot;)
            .allowedOrigins(&quot;http://localhost:5173&quot;)
            .allowedMethods(&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;, &quot;OPTIONS&quot;,&quot;PATCH&quot;)
            .allowedHeaders(&quot;*&quot;)
            .allowCredentials(true);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포트번호에서 날라오는 ajax는 허용해주겠다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래는 따로 webconfig.java 따로따로 해야하는데 나는 지금 security &amp;nbsp;거기다 통합해서 overide 해줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 강사님 코드처럼 여기 하나에다가 하는게 더 좋음 코드상&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1907&quot; data-origin-height=&quot;741&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LaEGD/btsPj6qT5PN/sLBjVxIg2LBXK7OwtIUS90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LaEGD/btsPj6qT5PN/sLBjVxIg2LBXK7OwtIUS90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LaEGD/btsPj6qT5PN/sLBjVxIg2LBXK7OwtIUS90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLaEGD%2FbtsPj6qT5PN%2FsLBjVxIg2LBXK7OwtIUS90%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;1907&quot; height=&quot;741&quot; data-origin-width=&quot;1907&quot; data-origin-height=&quot;741&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;</description>
      <category>중앙정보기술인재개발원/React</category>
      <author>soidev</author>
      <guid isPermaLink="true">https://soidev.tistory.com/41</guid>
      <comments>https://soidev.tistory.com/41#entry41comment</comments>
      <pubDate>Thu, 17 Jul 2025 10:12:16 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] | final project 댓글 구현</title>
      <link>https://soidev.tistory.com/40</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;오늘 할일&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;160&quot; data-start=&quot;129&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  Spring MVC 웹 애플리케이션 흐름 정리&lt;/span&gt;&lt;/h2&gt;
&lt;hr data-end=&quot;165&quot; data-start=&quot;162&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;201&quot; data-start=&quot;167&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  1. RequestMapping &amp;amp; 파라미터 처리&lt;/span&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;215&quot; data-start=&quot;203&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅ 요청 매핑&lt;/span&gt;&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752123678779&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RequestMapping(&quot;/process&quot;)
public String processForm(@ModelAttribute User user) {
    // form 데이터가 자동으로 User 객체에 매핑됨
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;433&quot; data-start=&quot;349&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;405&quot; data-start=&quot;349&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;@ModelAttribute: 폼에서 넘어온&amp;nbsp;&lt;b&gt;여러 개의 파라미터&lt;/b&gt;를 객체로 받아 처리&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;433&quot; data-start=&quot;406&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;DTO 객체와 필드명이 같으면 자동으로 매핑됨&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;452&quot; data-start=&quot;435&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅ 단일 파라미터 처리&lt;/span&gt;&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752123694087&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RequestMapping(&quot;/login&quot;)
public String login(@RequestParam String accountName,
                    @RequestParam String password) {
    // 단일 파라미터를 직접 받아옴
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;627&quot; data-start=&quot;624&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;660&quot; data-start=&quot;629&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  2. DB 연동 &amp;ndash; MyBatis 사용 흐름&lt;/span&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;682&quot; data-start=&quot;662&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅ MyBatis 구성 흐름&lt;/span&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-end=&quot;965&quot; data-start=&quot;683&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;803&quot; data-start=&quot;683&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;인터페이스 정의&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1752123764484&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface UserMapper {
    User selectByAccount(String accountName);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&amp;nbsp; &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&amp;nbsp; 2. XML 매핑 파일&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752123807617&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;select id=&quot;selectByAccount&quot; resultType=&quot;User&quot;&amp;gt;
    SELECT * FROM users WHERE account_name = #{accountName}
&amp;lt;/select&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-end=&quot;965&quot; data-start=&quot;683&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;965&quot; data-start=&quot;825&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;1023&quot; data-start=&quot;967&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;995&quot; data-start=&quot;967&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;쿼리 1개 = 메서드 1개 = XML 태그 1개&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1023&quot; data-start=&quot;996&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SQL은 XML에, 메서드는 인터페이스에 정의&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1028&quot; data-start=&quot;1025&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;1049&quot; data-start=&quot;1030&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  3. 로그인 처리 흐름&lt;/span&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-end=&quot;1238&quot; data-start=&quot;1051&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1087&quot; data-start=&quot;1051&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;로그인 요청 &amp;rarr;&amp;nbsp;@RequestParam으로 값 받기&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1115&quot; data-start=&quot;1088&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MyBatis로 DB에서&amp;nbsp;SELECT&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1214&quot; data-start=&quot;1116&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;로그인 성공 시&amp;nbsp;HttpSession에 사용자 정보 저장&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1238&quot; data-start=&quot;1215&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이후 인증 체크는 세션 기반으로 처리&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1752123831529&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;session.setAttribute(&quot;loginUser&quot;, user);&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;1243&quot; data-start=&quot;1240&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;1267&quot; data-start=&quot;1245&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  4. Redirect의 의미&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752123855118&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return &quot;redirect:/home&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;1392&quot; data-start=&quot;1307&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1327&quot; data-start=&quot;1307&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;HTML을 바로 응답하는 게 아님&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1353&quot; data-start=&quot;1328&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;브라우저에게&amp;nbsp;&lt;b&gt;다시 요청하라고 지시&lt;/b&gt;함&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1392&quot; data-start=&quot;1354&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉, 2번 요청이 발생함 (POST-Redirect-GET 패턴)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1397&quot; data-start=&quot;1394&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;1427&quot; data-start=&quot;1399&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  5. 게시판 &amp;amp; Thymeleaf 문법&lt;/span&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;1449&quot; data-start=&quot;1429&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅ 로그인 여부에 따른 처리&lt;/span&gt;&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752123868072&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div th:if=&quot;${session.loginUser != null}&quot;&amp;gt;
    &amp;lt;p th:text=&quot;${session.loginUser.name} + '님 환영합니다'&quot;&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;a th:href=&quot;@{/logout}&quot;&amp;gt;로그아웃&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;1667&quot; data-start=&quot;1610&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1628&quot; data-start=&quot;1610&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;th:if: 조건부 렌더링&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1648&quot; data-start=&quot;1629&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;th:text: 텍스트 출력&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1667&quot; data-start=&quot;1649&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;th:href: 링크 처리&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1672&quot; data-start=&quot;1669&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;1717&quot; data-start=&quot;1674&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  6. 수정(Form) 화면에서&amp;nbsp;hidden&amp;nbsp;태그가 필요한 이유&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752123884984&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;input type=&quot;hidden&quot; name=&quot;id&quot; th:value=&quot;${post.id}&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;1914&quot; data-start=&quot;1788&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1816&quot; data-start=&quot;1788&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;기본키(ID)&lt;/b&gt;&amp;nbsp;값을 숨겨서 전송하기 위함&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1868&quot; data-start=&quot;1817&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용자는 수정 시&amp;nbsp;id&amp;nbsp;값을 볼 수 없지만, 서버는 어떤 게시물을 수정할지 알아야 함&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1914&quot; data-start=&quot;1869&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;수정 처리는 보통&amp;nbsp;POST로 이루어지므로&amp;nbsp;id를 form에 포함해야 함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1919&quot; data-start=&quot;1916&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;1951&quot; data-start=&quot;1921&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  7. List vs Map &amp;ndash; 반복문 처리&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;2043&quot; data-start=&quot;1953&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1985&quot; data-start=&quot;1953&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;List: 반복문(th:each) 돌릴 때 사용&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;2043&quot; data-start=&quot;1986&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Map: 특정 key로 객체를 꺼낼 때 사용&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(DTO와 비슷한 방식으로 속성 접근 가능)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752123904793&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- List 예시 --&amp;gt;
&amp;lt;tr th:each=&quot;item : ${postList}&quot;&amp;gt;
    &amp;lt;td th:text=&quot;${item.title}&quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;

&amp;lt;!-- Map 예시 --&amp;gt;
&amp;lt;p th:text=&quot;${userMap['admin'].name}&quot;&amp;gt;&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;댓글 기능 구현&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1.먼저 db에다 fp_comment 테이블 만들기&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752123999191&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Create Table fp_comment(
id int primary key auto_increment,
articleId int,
userId int,
message VARCHAR(3000),
created_at DATETIME default NOW(),
FOREIGN KEY (articleId) REFERENCES fp_article(id) ON DELETE CASCADE,
FOREIGN KEY (userId) REFERENCES fp_user(id) ON DELETE CASCADE
);&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;background-color: #2f2f2f; color: #000000; text-align: start;&quot;&gt;
&lt;div style=&quot;background-color: #2f2f2f; color: #cccccc;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;foreign key 사용한 이유 : &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;외래키를 &amp;nbsp;댓글(CommentDto)을 등록할 때 이 댓글이 어떤 글(articleId)에 속하고, 누가(userId) 썼는지를 알아야 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;두 테이블을 연결하는 다리역할(다른테이블의 기본키 참조)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;데이터 무결성을 위해&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;on delete cascade : foreign key로 테이블 간 관계를 연결해놨을 때, 부모 테이블의 행이 삭제되면 자식 테이블의 연관된 행도 자동으로 삭제되게 해주는 옵션&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;처음엔 외래키 설정 안 했을 시 &amp;rarr;&amp;nbsp;articleId넘기려고할때 자꾸 db든 서버든 articleId가 null값이 들어감&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&amp;nbsp;1. articleId가 null로 저장되는 문제&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;폼이나 DTO에서는 값이 들어왔는데&lt;/b&gt;, DB에는 null로 들어가는 경우 많음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;왜? 외래키 없으면 DB가 &amp;ldquo;이 값 진짜 존재하는 article인지&amp;rdquo; 검사 안 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실수로 빠지거나, 컨트롤러에서 안 넣어줘도&amp;nbsp;&lt;b&gt;DB는 에러 없이 그냥 null 저장&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&amp;nbsp;2. 존재하지 않는 게시글 ID로 댓글 저장 가능&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예:&amp;nbsp;articleId = 999&amp;nbsp;이렇게 저장해도 DB는 에러 안 냄&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실제로는 그런 게시글이 없는데, 댓글만 DB에 저장됨 &amp;rarr;&amp;nbsp;&lt;b&gt;고아 데이터&lt;/b&gt;&amp;nbsp;생김&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&amp;nbsp;3. JOIN 쿼리에서 에러 나거나 결과 없음&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;댓글은 있는데&amp;nbsp;articleId가 null이거나, 존재하지 않는 게시글 ID라면?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조인할 때 결과 안 뜨거나 null 뜸 &amp;rarr; 프론트에서 에러 나기 딱 좋음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CommentDto 생성&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752124037807&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.ca.finalproject.dto;

import java.time.LocalDateTime;

import lombok.Data;

@Data
public class CommentDto {
    private int id; // 댓글 고유 번호 (PK)
    private int articleId; // 어떤 게시글에 달린 댓글인지
    private int userId; // 댓글 작성자
    private String message; // 댓글 내용
    private LocalDateTime createdAt; // 작성 시각
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;readArticlePage.html에다 form 추가&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752124081374&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;row mb-4&quot;&amp;gt;
    &amp;lt;div class=&quot;col-md-12&quot;&amp;gt;
        &amp;lt;form action=&quot;/board/writeComment&quot; method=&quot;post&quot;&amp;gt;
        &amp;lt;input type=&quot;hidden&quot; name=&quot;articleId&quot; th:value=&quot;${articleData.articleDto.id}&quot; /&amp;gt;
            &amp;lt;div class=&quot;input-group&quot;&amp;gt;
                &amp;lt;span class=&quot;input-group-text&quot;&amp;gt;
                	&amp;lt;i class=&quot;bi bi-chat-dots&quot;&amp;gt;&amp;lt;/i&amp;gt;
                &amp;lt;/span&amp;gt;

                &amp;lt;input type=&quot;text&quot; name=&quot;message&quot; class=&quot;form-control&quot; placeholder=&quot;댓글을 입력하세요&quot; required&amp;gt;

                &amp;lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot;&amp;gt;작성&amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/form&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용자는 그냥 댓글만 입력해 (&amp;lt;input type=&quot;text&quot; name=&quot;message&quot; ...&amp;gt;).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;form이 submit될 때&amp;nbsp;articleId도 함께 서버로 전송됨 (숨겨진 상태로).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버는 이&amp;nbsp;articleId를 보고 &amp;ldquo;이 댓글은 몇 번 게시글에 달린 거구나&amp;rdquo; 판단.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;댓글 DB에 저장할 때 그걸 같이 넣음.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;BoardController.java&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752124172908&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RequestMapping(&quot;writeComment&quot;)
public String writeComment(CommentDto commentDto,HttpSession session){
    UserDto sessionUser = (UserDto) session.getAttribute(&quot;sessionUserInfo&quot;);
    // System.out.println(&quot;sessionUser: &quot; + sessionUser);

    if (sessionUser == null) {
        System.out.println(&quot;로그인 세션이 없습니다.&quot;);
        return &quot;redirect:/user/loginPage&quot;; // 로그인 안된 경우 로그인 페이지로 보냄
    }

    commentDto.setUserId(sessionUser.getId());
    boardService.writeComment(commentDto);
    return &quot;redirect:/board/readArticlePage?id=&quot; + commentDto.getArticleId();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;BoardServiceImpl.java&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752124209309&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void writeComment(CommentDto commentDto){
	commentSqlMapper.insertComment(commentDto);
}

public List&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; getCommentListByArticleId(int articleId){
    List&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; resultList = new ArrayList&amp;lt;&amp;gt;();
    List&amp;lt;CommentDto&amp;gt; commentList = commentSqlMapper.findCommentsByArticleId(articleId);

    for(CommentDto commentDto : commentList){
        Map&amp;lt;String, Object&amp;gt; map = new HashMap&amp;lt;&amp;gt;();
        map.put(&quot;commentDto&quot;, commentDto);
        UserDto userDto = userSqlMapper.findUserById(commentDto.getUserId());
        map.put(&quot;userDto&quot;, userDto);
        resultList.add(map);
    }

    return resultList;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CommentSqlMapper.java&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752124233591&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.ca.finalproject.board.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import com.ca.finalproject.dto.CommentDto;

@Mapper
public interface CommentSqlMapper {
    public void insertComment(CommentDto commentDto);
    public List&amp;lt;CommentDto&amp;gt; findCommentsByArticleId(int articleId);
    public int countCommentsByArticleId(int articleId);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CommentSqlMapper.xml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752124249075&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&amp;gt;
&amp;lt;!DOCTYPE mapper PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot; &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&amp;gt;

&amp;lt;mapper namespace=&quot;com.ca.finalproject.board.mapper.CommentSqlMapper&quot;&amp;gt;
&amp;lt;select id=&quot;findCommentsByArticleId&quot; &amp;gt;
SELECT * FROM fp_comment WHERE articleId = #{articleId} ORDER BY created_at ASC
&amp;lt;/select&amp;gt;

&amp;lt;insert id=&quot;insertComment&quot;&amp;gt;
INSERT INTO fp_comment (articleId,userId,message)
VALUES (#{articleId},#{userId},#{message});
&amp;lt;/insert&amp;gt;

&amp;lt;select id=&quot;countCommentsByArticleId&quot;&amp;gt;
select count(*) from fp_comment where articleId=#{articleId}
&amp;lt;/select&amp;gt;

&amp;lt;/mapper&amp;gt;&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;근데 여기서 문제점 발견... db에단 commentCount가 연결안됨..&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5iYsK/btsNQPIOzS6/qXFewSHZSbZuvbBrL1LFa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5iYsK/btsNQPIOzS6/qXFewSHZSbZuvbBrL1LFa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5iYsK/btsNQPIOzS6/qXFewSHZSbZuvbBrL1LFa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5iYsK%2FbtsNQPIOzS6%2FqXFewSHZSbZuvbBrL1LFa0%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;1848&quot; height=&quot;516&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 화면 출력,콘솔 출력은 잘됨&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMfFjW/btsNOhfWQQ2/9SA9T2BM18tLVH2anLAXK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMfFjW/btsNOhfWQQ2/9SA9T2BM18tLVH2anLAXK1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1231&quot; data-origin-height=&quot;644&quot; data-filename=&quot;스크린샷 2025-05-08 오후 5.44.52.png&quot; width=&quot;724&quot; height=&quot;379&quot; data-widthpercent=&quot;40.35&quot; style=&quot;width: 39.881044%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMfFjW/btsNOhfWQQ2/9SA9T2BM18tLVH2anLAXK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMfFjW%2FbtsNOhfWQQ2%2F9SA9T2BM18tLVH2anLAXK1%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;1231&quot; height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxMrqJ/btsNQpDGURD/XiYRmOmmkX9m9FKLBDtpm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxMrqJ/btsNQpDGURD/XiYRmOmmkX9m9FKLBDtpm0/img.png&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;264&quot; data-is-animation=&quot;false&quot; width=&quot;661&quot; height=&quot;234&quot; data-widthpercent=&quot;59.65&quot; style=&quot;width: 58.956166%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxMrqJ/btsNQpDGURD/XiYRmOmmkX9m9FKLBDtpm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxMrqJ%2FbtsNQpDGURD%2FXiYRmOmmkX9m9FKLBDtpm0%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;746&quot; height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/div&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;뭐가 문제일까..&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다시 해보쟈&lt;/span&gt;&lt;/p&gt;</description>
      <category>중앙정보기술인재개발원/Spring</category>
      <category>db</category>
      <category>java</category>
      <category>spring</category>
      <category>댓글</category>
      <category>스프링</category>
      <author>soidev</author>
      <guid isPermaLink="true">https://soidev.tistory.com/40</guid>
      <comments>https://soidev.tistory.com/40#entry40comment</comments>
      <pubDate>Thu, 10 Jul 2025 14:11:14 +0900</pubDate>
    </item>
    <item>
      <title>[React] |  Vite 설치, JSX 문법, 컴포넌트까지 정리</title>
      <link>https://soidev.tistory.com/39</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;124&quot; data-start=&quot;88&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  React 프로젝트 시작하기 (Vite + React)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;143&quot; data-start=&quot;126&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. Node.js 설치&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;274&quot; data-start=&quot;144&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;219&quot; data-start=&quot;144&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;공식 웹사이트:&amp;nbsp;&lt;a href=&quot;https://nodejs.org/ko/download&quot;&gt;https://nodejs.org/ko/download&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;243&quot; data-start=&quot;220&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Node.js는 자바스크립트 실행 환경&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;274&quot; data-start=&quot;244&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;필수 도구:&amp;nbsp;node와&amp;nbsp;npm이 함께 설치됨&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1752118384599&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Node.js &amp;mdash; Node.js&amp;reg; 다운로드&quot; data-og-description=&quot;Node.js&amp;reg; is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.&quot; data-og-host=&quot;nodejs.org&quot; data-og-source-url=&quot;https://nodejs.org/ko/download&quot; data-og-url=&quot;https://nodejs.org/ko/download&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/24UU6/hyZjsoDr2P/XxKmU8wwkxW6F3VCY160p0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/lXuBW/hyZf9K3xlh/USKOW1NoluGyUk7tfUs8H0/img.png?width=224&amp;amp;height=256&amp;amp;face=0_0_224_256&quot;&gt;&lt;a href=&quot;https://nodejs.org/ko/download&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nodejs.org/ko/download&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/24UU6/hyZjsoDr2P/XxKmU8wwkxW6F3VCY160p0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/lXuBW/hyZf9K3xlh/USKOW1NoluGyUk7tfUs8H0/img.png?width=224&amp;amp;height=256&amp;amp;face=0_0_224_256');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Node.js &amp;mdash; Node.js&amp;reg; 다운로드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Node.js&amp;reg; is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nodejs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-end=&quot;279&quot; data-start=&quot;276&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;307&quot; data-start=&quot;281&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. Vite로 React 프로젝트 생성&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1752118374632&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm create vite@latest projectname1&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;386&quot; data-start=&quot;356&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;386&quot; data-start=&quot;358&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;projectname1은 원하는 프로젝트 폴더명&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-end=&quot;391&quot; data-start=&quot;388&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;407&quot; data-start=&quot;393&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 프로젝트 세팅&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752118485815&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd projectname1
npm install         # 필요한 패키지 설치
npm run dev         # 개발 서버 실행 (Ctrl + F5)​&lt;/code&gt;&lt;/pre&gt;
&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;532&quot; data-start=&quot;513&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;532&quot; data-start=&quot;513&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버 종료:&amp;nbsp;Ctrl + C&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;537&quot; data-start=&quot;534&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;555&quot; data-start=&quot;539&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  주요 파일 및 구조&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;580&quot; data-start=&quot;557&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;package.json&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;638&quot; data-start=&quot;581&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;609&quot; data-start=&quot;581&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JavaScript 프로젝트의&amp;nbsp;&lt;b&gt;설정 파일&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;638&quot; data-start=&quot;610&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Java에서의&amp;nbsp;build.gradle과 유사&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;659&quot; data-start=&quot;640&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &amp;nbsp;index.html&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;734&quot; data-start=&quot;660&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;678&quot; data-start=&quot;660&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;React가 렌더링되는 진입점&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;734&quot; data-start=&quot;679&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;lt;div id=&quot;root&quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;nbsp;&amp;larr; 여기에 React 앱이 붙음 (&lt;b&gt;가장 중요&lt;/b&gt;)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;739&quot; data-start=&quot;736&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;759&quot; data-start=&quot;741&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JSX와 컴포넌트 기본&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;770&quot; data-start=&quot;761&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JSX란?&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;833&quot; data-start=&quot;771&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;798&quot; data-start=&quot;771&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JavaScript + HTML = JSX&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;833&quot; data-start=&quot;799&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JavaScript 내부에서 HTML처럼 마크업 작성 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;848&quot; data-start=&quot;835&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JSX 문법 특징&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;1308&quot; data-start=&quot;849&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1223&quot; data-start=&quot;849&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;컴포넌트는 하나의 최상위 엘리먼트만 반환 가능&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;❌ 오류:&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;969&quot; data-start=&quot;895&quot;&gt;
&lt;div&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;jsx&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1752118560832&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return (
  &amp;lt;div&amp;gt;안녕&amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;안녕&amp;lt;/div&amp;gt;
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;✅ 올바른 방식:&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752118582365&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return (
  &amp;lt;div&amp;gt;
    &amp;lt;div&amp;gt;야호&amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;반갑&amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;또는 빈 태그&amp;nbsp;&amp;lt;&amp;gt; &amp;lt;/&amp;gt;&amp;nbsp;사용 가능:&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752118599365&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return (
  &amp;lt;&amp;gt;
    &amp;lt;div&amp;gt;야호&amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;반갑&amp;lt;/div&amp;gt;
  &amp;lt;/&amp;gt;
);​&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;1308&quot; data-start=&quot;849&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1308&quot; data-start=&quot;1225&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;닫는 태그 필수&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1308&quot; data-start=&quot;1244&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1280&quot; data-start=&quot;1244&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;lt;img&amp;gt;&amp;nbsp;태그 등도&amp;nbsp;&amp;lt;img /&amp;gt;&amp;nbsp;형식으로 닫아야 함&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1308&quot; data-start=&quot;1285&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;lt;br&amp;gt;&amp;nbsp;&amp;rarr;&amp;nbsp;&amp;lt;br /&amp;gt;로 작성&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1313&quot; data-start=&quot;1310&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;1331&quot; data-start=&quot;1315&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;  스타일 및 컴포넌트&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;1346&quot; data-start=&quot;1333&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;index.css&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;1377&quot; data-start=&quot;1347&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1360&quot; data-start=&quot;1347&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;초기 설정 CSS&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1360&quot; data-start=&quot;1347&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;원하면 전부 삭제해도 무방&amp;nbsp;&lt;/span&gt;&lt;hr data-end=&quot;1382&quot; data-start=&quot;1379&quot; data-ke-style=&quot;style1&quot; /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;컴포넌트 작성 (파일 확장자:&amp;nbsp;.jsx)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot; data-state=&quot;closed&quot;&gt;&lt;/span&gt;&lt;/div&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;리액트전용 컴포넌트파일 : jsx 파일을 만들어야함&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1752114692341&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import './MyAppButton.css';

//매우 작은 컴포넌트
//컴포넌트 안쪽에 호출할수있고 그 안에 또 호출할 수 있음
//children = 정해져 있는 변수 명
function MyAppButton({children}) {
    return(
        &amp;lt;div className='my-button'&amp;gt;{children}&amp;lt;/div&amp;gt;
    )
}

export default MyAppButton;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-end=&quot;1718&quot; data-start=&quot;1622&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1652&quot; data-start=&quot;1622&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;children: 컴포넌트 내부에서 전달된 내용&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1692&quot; data-start=&quot;1653&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;{}: JSX 안에서&amp;nbsp;&lt;b&gt;JavaScript 표현식&lt;/b&gt;을 사용함&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1718&quot; data-start=&quot;1693&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;재사용 가능한 작은 UI 단위 만들기 용이&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JSX 참고: &lt;a href=&quot;https://ko.react.dev/learn/writing-markup-with-jsx&quot;&gt;https://ko.react.dev/learn/writing-markup-with-jsx&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1752111688998&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;JSX로 마크업 작성하기 &amp;ndash; React&quot; data-og-description=&quot;The library for web and native user interfaces&quot; data-og-host=&quot;ko.react.dev&quot; data-og-source-url=&quot;https://ko.react.dev/learn/writing-markup-with-jsx&quot; data-og-url=&quot;https://ko.react.dev/learn/writing-markup-with-jsx&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Ub29U/hyZjAUvFFy/nMzDwdkOLQVjOYS61zNd8k/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/cQgvHC/hyZjqK8igy/HIE1PpCmMhaiFJYcWbphD1/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567&quot;&gt;&lt;a href=&quot;https://ko.react.dev/learn/writing-markup-with-jsx&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.react.dev/learn/writing-markup-with-jsx&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Ub29U/hyZjAUvFFy/nMzDwdkOLQVjOYS61zNd8k/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/cQgvHC/hyZjqK8igy/HIE1PpCmMhaiFJYcWbphD1/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;JSX로 마크업 작성하기 &amp;ndash; React&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The library for web and native user interfaces&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.react.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>중앙정보기술인재개발원/React</category>
      <category>dev</category>
      <category>react</category>
      <category>개발</category>
      <category>리액트</category>
      <author>soidev</author>
      <guid isPermaLink="true">https://soidev.tistory.com/39</guid>
      <comments>https://soidev.tistory.com/39#entry39comment</comments>
      <pubDate>Thu, 10 Jul 2025 13:52:39 +0900</pubDate>
    </item>
    <item>
      <title>[Open AI] API 키 발급하기 &amp;amp; 카드 등록</title>
      <link>https://soidev.tistory.com/38</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프로젝트 하면서 open ai 를 한번 사용해보기로했다&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://openai.com/&quot;&gt;https://openai.com/&lt;/a&gt; 여기 메인 화면에서 로그인을 하면 아래 사진에 있는 화면으로 이동한다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;977&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daXiSy/btsOTmtCqe6/V15Sa5REkIxm7PPkBEKSV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daXiSy/btsOTmtCqe6/V15Sa5REkIxm7PPkBEKSV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daXiSy/btsOTmtCqe6/V15Sa5REkIxm7PPkBEKSV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaXiSy%2FbtsOTmtCqe6%2FV15Sa5REkIxm7PPkBEKSV1%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;733&quot; height=&quot;375&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;977&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 화면으로 들어올 것이다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;검색으로 API Keys 라고 치면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아니면 &lt;a href=&quot;https://platform.openai.com/account/api-keys&quot;&gt;https://platform.openai.com/account/api-keys&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;996&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QUyER/btsOTJoAdkB/Y1n9RYklNBCPDymIoNq5a0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QUyER/btsOTJoAdkB/Y1n9RYklNBCPDymIoNq5a0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QUyER/btsOTJoAdkB/Y1n9RYklNBCPDymIoNq5a0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQUyER%2FbtsOTJoAdkB%2FY1n9RYklNBCPDymIoNq5a0%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;793&quot; height=&quot;414&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;996&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 화면으로 들어온다 create new secret key 누르면&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;545&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VZfvB/btsOS9Oh3kL/hiwRgJ7NkxZ0rRclwDIaPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VZfvB/btsOS9Oh3kL/hiwRgJ7NkxZ0rRclwDIaPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VZfvB/btsOS9Oh3kL/hiwRgJ7NkxZ0rRclwDIaPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVZfvB%2FbtsOS9Oh3kL%2FhiwRgJ7NkxZ0rRclwDIaPk%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;576&quot; height=&quot;545&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;545&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;name 은 표기되어 있는대로 Optional 항목이기 1때문에 , 꼭 써줄 필요 없지만 나중에 구분하기 위해 추가했다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그리고 create secret key 누르면 api 발급 완료!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;2. 내 키 목록 확인하기&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;처음 만들어둔 키가 user 기반이라&amp;nbsp; Settings &amp;gt; Your profile &amp;gt; User API keys로 접근하여 확인이 가능했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프로젝트 API 키 목록은 API keys에서 바로 확인할 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프로젝트를 사용하여 API 키를 정리할 수 있는데, 관련 공식 문서는 아래 링크를 참고하면 된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://help.openai.com/en/articles/9186755-managing-your-work-in-the-api-platform-with-projects&quot;&gt;https://help.openai.com/en/articles/9186755-managing-your-work-in-the-api-platform-with-projects&lt;/a&gt;&amp;nbsp;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이제 이 key 발급한 걸 활용하려면 카드 등록을 해야한다&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDIFuj/btsOSxB7Vhm/XIs5neBEkWbRIqwIA9hQG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDIFuj/btsOSxB7Vhm/XIs5neBEkWbRIqwIA9hQG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDIFuj/btsOSxB7Vhm/XIs5neBEkWbRIqwIA9hQG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDIFuj%2FbtsOSxB7Vhm%2FXIs5neBEkWbRIqwIA9hQG0%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;1899&quot; height=&quot;790&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Billing 페이지로 이동 -&amp;gt; add payment details 누르고 카드 정보 입력&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정보 입력 다음 누르면&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;677&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/laoOR/btsOUsGo0vc/P2g0ItRluwuTpUwXnoqwaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/laoOR/btsOUsGo0vc/P2g0ItRluwuTpUwXnoqwaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/laoOR/btsOUsGo0vc/P2g0ItRluwuTpUwXnoqwaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlaoOR%2FbtsOUsGo0vc%2FP2g0ItRluwuTpUwXnoqwaK%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;526&quot; height=&quot;677&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;677&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 화면이 뜬다 나는 지금 제일 최소로 5달러만 적었다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자동결제하면 문제 될 수 있으니 절대 yes 금지!!&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이제 이 api를 활용하기 위해 환경변수 설정&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;터미널 키기&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1751005421737&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;touch ~/.zshrc   &amp;lt;= 파일 만들기
nano ~/.zshrc     여기서 파일 안에 환경변수 export 하기
export OPENAI_API_KEY='key'
source ~/.zshrc
echo $OPENAI_API_KEY&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;여기까지 다하면 내 키발급한 코드가 나올것임 그럼 성공!!&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고용&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://alltipz.com/6&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://alltipz.com/6&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751003091203&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;open ai api key 발급하기 (feat. make 로 자동화 연동)&quot; data-og-description=&quot;1. open ai 홈페이지에 가서 회원가입을 해준다.&amp;nbsp;https://platform.openai.com/docs/overview회원가입을 마치고 나면 오버뷰 화면을 볼 수 있다.&amp;nbsp;&amp;nbsp;&amp;nbsp;2. 키 발급을 위해 하단 링크로 키 관리 페이지로 입장. (혹&quot; data-og-host=&quot;alltipz.com&quot; data-og-source-url=&quot;https://alltipz.com/6&quot; data-og-url=&quot;https://alltipz.com/6&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/opdxl/hyZf39HEVB/OluUxsi38g8msnSjSE6bXK/img.png?width=800&amp;amp;height=430&amp;amp;face=0_0_800_430,https://scrap.kakaocdn.net/dn/bb1sa1/hyZfyPujr3/joUQNWmyMIEMzwtGR52OH0/img.png?width=800&amp;amp;height=430&amp;amp;face=0_0_800_430,https://scrap.kakaocdn.net/dn/Jh5bx/hyZcmJTz39/kxOzabhp2EkXatoyQODZc1/img.png?width=1603&amp;amp;height=728&amp;amp;face=0_0_1603_728&quot;&gt;&lt;a href=&quot;https://alltipz.com/6&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://alltipz.com/6&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/opdxl/hyZf39HEVB/OluUxsi38g8msnSjSE6bXK/img.png?width=800&amp;amp;height=430&amp;amp;face=0_0_800_430,https://scrap.kakaocdn.net/dn/bb1sa1/hyZfyPujr3/joUQNWmyMIEMzwtGR52OH0/img.png?width=800&amp;amp;height=430&amp;amp;face=0_0_800_430,https://scrap.kakaocdn.net/dn/Jh5bx/hyZcmJTz39/kxOzabhp2EkXatoyQODZc1/img.png?width=1603&amp;amp;height=728&amp;amp;face=0_0_1603_728');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;open ai api key 발급하기 (feat. make 로 자동화 연동)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. open ai 홈페이지에 가서 회원가입을 해준다.&amp;nbsp;https://platform.openai.com/docs/overview회원가입을 마치고 나면 오버뷰 화면을 볼 수 있다.&amp;nbsp;&amp;nbsp;&amp;nbsp;2. 키 발급을 위해 하단 링크로 키 관리 페이지로 입장. (혹&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;alltipz.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>중앙정보기술인재개발원</category>
      <category>AI</category>
      <category>API</category>
      <category>ChatGPT</category>
      <category>gpt</category>
      <category>Key</category>
      <category>OpenAI</category>
      <category>키발급</category>
      <author>soidev</author>
      <guid isPermaLink="true">https://soidev.tistory.com/38</guid>
      <comments>https://soidev.tistory.com/38#entry38comment</comments>
      <pubDate>Fri, 27 Jun 2025 15:24:48 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 카카오페이 단건결제 API</title>
      <link>https://soidev.tistory.com/37</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buO0Q6/btsOLpperR0/gMVLP8yBoqXz7r1f0WqxQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buO0Q6/btsOLpperR0/gMVLP8yBoqXz7r1f0WqxQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buO0Q6/btsOLpperR0/gMVLP8yBoqXz7r1f0WqxQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuO0Q6%2FbtsOLpperR0%2FgMVLP8yBoqXz7r1f0WqxQ1%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;1342&quot; height=&quot;575&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;953&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q0Nlq/btsOKlg9aBK/vQ2lLqriMu5LwOAtevqd81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q0Nlq/btsOKlg9aBK/vQ2lLqriMu5LwOAtevqd81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q0Nlq/btsOKlg9aBK/vQ2lLqriMu5LwOAtevqd81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq0Nlq%2FbtsOKlg9aBK%2FvQ2lLqriMu5LwOAtevqd81%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;953&quot; height=&quot;235&quot; data-origin-width=&quot;953&quot; data-origin-height=&quot;235&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;카카오페이 api사용하려면 일단 카카오페이 파트너에 가입 후&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;애플리케이션 등록&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;web 테스트용 도메인 등록&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;테스트용으로서 Secret key(dev) 사용&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;내 예제 &amp;rarr; 기부버튼 누르면 자동으로 만원씩 후원하게끔 할 것!&amp;lt;단건결제&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1651&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nmgjH/btsOJIKtWMZ/IXyuPyBV7BVkNbNNoKkAIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nmgjH/btsOJIKtWMZ/IXyuPyBV7BVkNbNNoKkAIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nmgjH/btsOJIKtWMZ/IXyuPyBV7BVkNbNNoKkAIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnmgjH%2FbtsOJIKtWMZ%2FIXyuPyBV7BVkNbNNoKkAIK%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;1651&quot; height=&quot;808&quot; data-origin-width=&quot;1651&quot; data-origin-height=&quot;808&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;내 예제 hml&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고용 문서 : &lt;a href=&quot;https://developers.kakaopay.com/docs/payment/online/single-payment&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developers.kakaopay.com/docs/payment/online/single-payment&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1750401631039&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;카카오페이 | 개발자센터&quot; data-og-description=&quot;새로운 기회와 가치를 함께 만들어봐요&quot; data-og-host=&quot;developers.kakaopay.com&quot; data-og-source-url=&quot;https://developers.kakaopay.com/docs/payment/online/single-payment&quot; data-og-url=&quot;https://developers.kakaopay.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/1m1ml/hyY8T80hKt/u6zQKDjDuKI3xu7Ws0ZSAk/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://developers.kakaopay.com/docs/payment/online/single-payment&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.kakaopay.com/docs/payment/online/single-payment&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/1m1ml/hyY8T80hKt/u6zQKDjDuKI3xu7Ws0ZSAk/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;카카오페이 | 개발자센터&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;새로운 기회와 가치를 함께 만들어봐요&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.kakaopay.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;application.properties&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750401682460&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# application.properties

kakao.api.secret-key=카카오에서발급받은REST_API_키
kakao.api.cid=TC0ONETIME
kakao.api.approval-url=http://localhost:8080/donation/success
kakao.api.cancel-url=http://localhost:8080/donation/cancel
kakao.api.fail-url=http://localhost:8080/donation/fail&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;각자 발급받은 테스트용 어플리케이션키 넣기&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;kakao.api ~ 이건 변수명이라 다른거로 가능&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;applicaiotn.properties에선 다 변수명이라 자유롭게 쓸수있음!&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://velog.io/@ryuneng2/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%8E%98%EC%9D%B4-API-%EC%97%B0%EB%8F%99-%ED%8C%9D%EC%97%85%EC%B0%BD%EB%9D%84%EC%9A%B0%EA%B8%B0-%EA%B2%B0%EC%A0%9C%EC%8A%B9%EC%9D%B8-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@ryuneng2/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%8E%98%EC%9D%B4-API-%EC%97%B0%EB%8F%99-%ED%8C%9D%EC%97%85%EC%B0%BD%EB%9D%84%EC%9A%B0%EA%B8%B0-%EA%B2%B0%EC%A0%9C%EC%8A%B9%EC%9D%B8-%EA%B5%AC%ED%98%84&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1750407813631&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Spring Boot] 카카오페이 API 연동 - 팝업창 띄우기 및 결제승인까지&quot; data-og-description=&quot;카카오페이를 구현하기 위해 여러 자료를 참고해서 코드를 작성해보았으나, 작동하지 않았다. 꽤 최신 블로그 글(약 6개월 전 포스팅)을 참고해보기도 했지만, 여전히 오류만 날 뿐이었다. 도대&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@ryuneng2/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%8E%98%EC%9D%B4-API-%EC%97%B0%EB%8F%99-%ED%8C%9D%EC%97%85%EC%B0%BD%EB%9D%84%EC%9A%B0%EA%B8%B0-%EA%B2%B0%EC%A0%9C%EC%8A%B9%EC%9D%B8-%EA%B5%AC%ED%98%84&quot; data-og-url=&quot;https://velog.io/@ryuneng2/카카오페이-API-연동-팝업창띄우기-결제승인-구현&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbYiEV/hyZckRqzh3/GJ8iGOyvpe8nFPHGKYaaiK/img.png?width=979&amp;amp;height=805&amp;amp;face=0_0_979_805,https://scrap.kakaocdn.net/dn/bkMMxD/hyZbzHV1zV/TVV3GbELNLOLS1x4eny1t1/img.png?width=979&amp;amp;height=805&amp;amp;face=0_0_979_805,https://scrap.kakaocdn.net/dn/XJM9g/hyZbtOtpiK/jANOXFmkMznU8V5MZ34ka0/img.png?width=1913&amp;amp;height=862&amp;amp;face=0_0_1913_862&quot;&gt;&lt;a href=&quot;https://velog.io/@ryuneng2/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%8E%98%EC%9D%B4-API-%EC%97%B0%EB%8F%99-%ED%8C%9D%EC%97%85%EC%B0%BD%EB%9D%84%EC%9A%B0%EA%B8%B0-%EA%B2%B0%EC%A0%9C%EC%8A%B9%EC%9D%B8-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@ryuneng2/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%8E%98%EC%9D%B4-API-%EC%97%B0%EB%8F%99-%ED%8C%9D%EC%97%85%EC%B0%BD%EB%9D%84%EC%9A%B0%EA%B8%B0-%EA%B2%B0%EC%A0%9C%EC%8A%B9%EC%9D%B8-%EA%B5%AC%ED%98%84&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbYiEV/hyZckRqzh3/GJ8iGOyvpe8nFPHGKYaaiK/img.png?width=979&amp;amp;height=805&amp;amp;face=0_0_979_805,https://scrap.kakaocdn.net/dn/bkMMxD/hyZbzHV1zV/TVV3GbELNLOLS1x4eny1t1/img.png?width=979&amp;amp;height=805&amp;amp;face=0_0_979_805,https://scrap.kakaocdn.net/dn/XJM9g/hyZbtOtpiK/jANOXFmkMznU8V5MZ34ka0/img.png?width=1913&amp;amp;height=862&amp;amp;face=0_0_1913_862');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Spring Boot] 카카오페이 API 연동 - 팝업창 띄우기 및 결제승인까지&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;카카오페이를 구현하기 위해 여러 자료를 참고해서 코드를 작성해보았으나, 작동하지 않았다. 꽤 최신 블로그 글(약 6개월 전 포스팅)을 참고해보기도 했지만, 여전히 오류만 날 뿐이었다. 도대&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이거 보고 참고&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;902&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8dIq9/btsOLgAbjLX/oK0S7SUNifDrvb7NdYooj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8dIq9/btsOLgAbjLX/oK0S7SUNifDrvb7NdYooj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8dIq9/btsOLgAbjLX/oK0S7SUNifDrvb7NdYooj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8dIq9%2FbtsOLgAbjLX%2FoK0S7SUNifDrvb7NdYooj0%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;1894&quot; height=&quot;902&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;902&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;계속 오류가 떴음 왜냐 구버전인 예제가 많아서 초보개발자인 나에겐... 오류 고치는게 너무 오래걸렸음&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;오류가 났던 코드..&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750435832995&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.choongang.univ.barrierfree.donation.service;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

@Service
public class DonationService {

    @Value(&quot;${kakao.api.secret-key}&quot;)
    private String secretKey;

    @Value(&quot;${kakao.api.cid}&quot;)
    private String cid;

    @Value(&quot;${kakao.api.approval-url}&quot;)
    private String approvalUrl;

    @Value(&quot;${kakao.api.cancel-url}&quot;)
    private String cancelUrl;

    @Value(&quot;${kakao.api.fail-url}&quot;)
    private String failUrl;

    public Map&amp;lt;String, Object&amp;gt; kakaoPayReady() {
        RestTemplate restTemplate = new RestTemplate();

        // 1. Header
        HttpHeaders headers = new HttpHeaders();
        headers.set(&quot;Authorization&quot;, &quot;SecretKey &quot; + secretKey);  
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // ✅ 중요

        System.out.println(&quot;Headers: &quot; + headers.toString());

        // 2. Body
        MultiValueMap&amp;lt;String, String&amp;gt; body = new LinkedMultiValueMap&amp;lt;&amp;gt;();
        body.add(&quot;cid&quot;, cid);
        body.add(&quot;partner_order_id&quot;, &quot;donation_001&quot;);
        body.add(&quot;partner_user_id&quot;, &quot;anonymous_donor&quot;);
        body.add(&quot;item_name&quot;, &quot;장애학생 기부&quot;);
        body.add(&quot;quantity&quot;, &quot;1&quot;);
        body.add(&quot;total_amount&quot;, &quot;10000&quot;);
        body.add(&quot;vat_amount&quot;, &quot;0&quot;);
        body.add(&quot;tax_free_amount&quot;, &quot;0&quot;);
        body.add(&quot;approval_url&quot;, approvalUrl);
        body.add(&quot;cancel_url&quot;, cancelUrl);
        body.add(&quot;fail_url&quot;, failUrl);

        HttpEntity&amp;lt;MultiValueMap&amp;lt;String, String&amp;gt;&amp;gt; request = new HttpEntity&amp;lt;&amp;gt;(body, headers);

        String url = &quot;https://open-api.kakaopay.com/online/v1/payment/ready&quot;;

        ResponseEntity&amp;lt;Map&amp;gt; response = restTemplate.postForEntity(url, request, Map.class);

        return response.getBody();
    }

    public Map&amp;lt;String, Object&amp;gt; kakaoPayApprove(String tid, String pgToken) {
        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();
        // headers.set(&quot;Authorization&quot;, secretKey);
        headers.set(&quot;Authorization&quot;, &quot;KakaoAK &quot; + secretKey);
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap&amp;lt;String, String&amp;gt; body = new LinkedMultiValueMap&amp;lt;&amp;gt;();
        body.add(&quot;cid&quot;, cid);
        body.add(&quot;tid&quot;, tid);
        body.add(&quot;partner_order_id&quot;, &quot;donation_001&quot;);
        body.add(&quot;partner_user_id&quot;, &quot;anonymous_donor&quot;);
        body.add(&quot;pg_token&quot;, pgToken);

        HttpEntity&amp;lt;MultiValueMap&amp;lt;String, String&amp;gt;&amp;gt; request = new HttpEntity&amp;lt;&amp;gt;(body, headers);

        // String url = &quot;https://open-api.kakaopay.com/online/v1/payment/approve&quot;;
        String url = &quot;https://kapi.kakao.com/v1/payment/approve&quot;;
        


        ResponseEntity&amp;lt;Map&amp;gt; response = restTemplate.postForEntity(url, request, Map.class);

        return response.getBody();
    }

    private HttpHeaders getHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.set(&quot;Authorization&quot;, &quot;카카오페이 개발자센터에서 발급받은 Secret key(dev) 입력&quot;);
        headers.set(&quot;Content-type&quot;, &quot;application/json&quot;);

        return headers;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;오류가 난 이유 &amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;url도 다르고 header.set하는게 구버전으로 했었음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;u&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;https://open-api.kakaopay.com/online/v1/payment/~&lt;/span&gt;&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;content-type도 구버전으로..&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;u&gt;&lt;b&gt;HashMap&lt;/b&gt;&lt;/u&gt;으로도 해야함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만약에 하시면 이제 지금 버전으로 꼭 문서 꼼꼼히 읽으셔야겠음..!!&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;adminkey가 아니라 이제 &lt;u&gt;&lt;b&gt;secret-key&lt;/b&gt;&lt;/u&gt;&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그래서 지금 고친 버전&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750436171821&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.choongang.univ.barrierfree.donation.service;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

@Service
public class DonationService {

    @Value(&quot;${kakao.api.secret-key}&quot;)
    private String secretKey;

    @Value(&quot;${kakao.api.cid}&quot;)
    private String cid;

    @Value(&quot;${kakao.api.approval-url}&quot;)
    private String approvalUrl;

    @Value(&quot;${kakao.api.cancel-url}&quot;)
    private String cancelUrl;

    @Value(&quot;${kakao.api.fail-url}&quot;)
    private String failUrl;

    public Map&amp;lt;String, Object&amp;gt; kakaoPayReady() {
        RestTemplate restTemplate = new RestTemplate();

        // 1. Header
        HttpHeaders headers = new HttpHeaders();
        headers.set(&quot;Authorization&quot;, &quot;SECRET_KEY &quot; + secretKey);
        headers.setContentType(MediaType.APPLICATION_JSON);

        Map&amp;lt;String, Object&amp;gt; body = new HashMap&amp;lt;&amp;gt;();
        body.put(&quot;cid&quot;, cid);
        body.put(&quot;partner_order_id&quot;, &quot;donation_001&quot;);
        body.put(&quot;partner_user_id&quot;, &quot;anonymous_donor&quot;);
        body.put(&quot;item_name&quot;, &quot;장애학생 기부&quot;);
        body.put(&quot;quantity&quot;, 1);
        body.put(&quot;total_amount&quot;, 10000);
        body.put(&quot;vat_amount&quot;, 0);
        body.put(&quot;tax_free_amount&quot;, 0);
        body.put(&quot;approval_url&quot;, approvalUrl);
        body.put(&quot;cancel_url&quot;, cancelUrl);
        body.put(&quot;fail_url&quot;, failUrl);

        HttpEntity&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; request = new HttpEntity&amp;lt;&amp;gt;(body, headers);

        String url = &quot;https://open-api.kakaopay.com/online/v1/payment/ready&quot;;

        ResponseEntity&amp;lt;Map&amp;gt; response = restTemplate.postForEntity(url, request, Map.class);

        return response.getBody();
    }

    public Map&amp;lt;String, Object&amp;gt; kakaoPayApprove(String tid, String pgToken) {
        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();
        headers.set(&quot;Authorization&quot;, &quot;SECRET_KEY &quot; + secretKey);
        headers.setContentType(MediaType.APPLICATION_JSON);

        Map&amp;lt;String, Object&amp;gt; body = new HashMap&amp;lt;&amp;gt;();
        body.put(&quot;cid&quot;, cid);
        body.put(&quot;tid&quot;, tid);
        body.put(&quot;partner_order_id&quot;, &quot;donation_001&quot;);
        body.put(&quot;partner_user_id&quot;, &quot;anonymous_donor&quot;);
        body.put(&quot;pg_token&quot;, pgToken);

        HttpEntity&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; request = new HttpEntity&amp;lt;&amp;gt;(body, headers);

        // String url = &quot;https://open-api.kakaopay.com/online/v1/payment/approve&quot;;
        // String url = &quot;https://kapi.kakao.com/v1/payment/approve&quot;;
        String url = &quot;https://open-api.kakaopay.com/online/v1/payment/approve&quot;;

        ResponseEntity&amp;lt;Map&amp;gt; response = restTemplate.postForEntity(url, request, Map.class);

        return response.getBody();
    }

    private HttpHeaders getHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.set(&quot;Authorization&quot;, &quot;카카오페이 개발자센터에서 발급받은 Secret key(dev) 입력&quot;);
        headers.set(&quot;Content-type&quot;, &quot;application/json&quot;);

        return headers;
    }
}&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Controller&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1750436202352&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.choongang.univ.barrierfree.donation.controller;

import java.util.Map;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.HttpClientErrorException;

import com.choongang.univ.barrierfree.donation.service.DonationService;

import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping(&quot;api/donation&quot;)
@RequiredArgsConstructor
public class DonationRestController {

    private final DonationService donationService;

    // 카카오페이 결제 준비
    @PostMapping(&quot;/kakaoPayReady&quot;)
    public ResponseEntity&amp;lt;?&amp;gt; kakaoPayReady(HttpSession session) {
        try {
            Map&amp;lt;String, Object&amp;gt; response = donationService.kakaoPayReady();
            session.setAttribute(&quot;tid&quot;, response.get(&quot;tid&quot;)); // 결제 승인용 TID 저장
            return ResponseEntity.ok(response);
        } catch (HttpClientErrorException e) {
            return ResponseEntity
                .status(e.getStatusCode())
                .body(Map.of(&quot;error&quot;, &quot;카카오페이 준비 요청 실패&quot;, &quot;message&quot;, e.getResponseBodyAsString()));
        } catch (Exception e) {
            return ResponseEntity
                .internalServerError()
                .body(Map.of(&quot;error&quot;, &quot;서버 오류&quot;, &quot;message&quot;, e.getMessage()));
        }
    }

    // 카카오페이 결제 성공 후 호출
    @GetMapping(&quot;/success&quot;)
    public ResponseEntity&amp;lt;?&amp;gt; kakaoPaySuccess(@RequestParam(&quot;pg_token&quot;) String pgToken, HttpSession session) {
        String tid = (String) session.getAttribute(&quot;tid&quot;);
        if (tid == null) {
            return ResponseEntity
                .badRequest()
                .body(Map.of(&quot;error&quot;, &quot;TID 누락&quot;, &quot;message&quot;, &quot;결제 준비가 선행되지 않았습니다.&quot;));
        }

        try {
            Map&amp;lt;String, Object&amp;gt; response = donationService.kakaoPayApprove(tid, pgToken);
            return ResponseEntity.ok(response);
        } catch (HttpClientErrorException e) {
            return ResponseEntity
                .status(e.getStatusCode())
                .body(Map.of(&quot;error&quot;, &quot;카카오페이 승인 요청 실패&quot;, &quot;message&quot;, e.getResponseBodyAsString()));
        } catch (Exception e) {
            return ResponseEntity
                .internalServerError()
                .body(Map.of(&quot;error&quot;, &quot;서버 오류&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;1526&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqb19x/btsOK9gHLq1/DdP4hK5lKyOeCHZ3y6CUxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqb19x/btsOK9gHLq1/DdP4hK5lKyOeCHZ3y6CUxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqb19x/btsOK9gHLq1/DdP4hK5lKyOeCHZ3y6CUxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcqb19x%2FbtsOK9gHLq1%2FDdP4hK5lKyOeCHZ3y6CUxK%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;524&quot; height=&quot;763&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;1526&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhwlfx/btsOKFmIIwF/wKcyI497BjJx8TGGzTjSH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhwlfx/btsOKFmIIwF/wKcyI497BjJx8TGGzTjSH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhwlfx/btsOKFmIIwF/wKcyI497BjJx8TGGzTjSH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbhwlfx%2FbtsOKFmIIwF%2FwKcyI497BjJx8TGGzTjSH1%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;240&quot; height=&quot;520&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&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;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;성공!!!!&lt;/span&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;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;근데 내가 response로 받아서 결제완료하면 페이지가 html이 아니라 json 데이터값들 보여지는게 뜸&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;이제 성공 페이지 구현&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;1426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DEeau/btsOL9fUqZK/z9yKKsD3Tfi7sebohWGRK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DEeau/btsOL9fUqZK/z9yKKsD3Tfi7sebohWGRK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DEeau/btsOL9fUqZK/z9yKKsD3Tfi7sebohWGRK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDEeau%2FbtsOL9fUqZK%2Fz9yKKsD3Tfi7sebohWGRK1%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;1896&quot; height=&quot;1426&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;1426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;완료!!!! 휴우 드디어 끝났다!!!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;날짜만 약간 바꾸면 결제 test 끝@@&lt;/span&gt;&lt;/p&gt;</description>
      <category>중앙정보기술인재개발원/Spring</category>
      <category>API</category>
      <category>kakaoapi</category>
      <category>KakaoPay</category>
      <category>spring</category>
      <category>개발자</category>
      <category>코린이</category>
      <author>soidev</author>
      <guid isPermaLink="true">https://soidev.tistory.com/37</guid>
      <comments>https://soidev.tistory.com/37#entry37comment</comments>
      <pubDate>Sat, 21 Jun 2025 02:30:56 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] final project_3차</title>
      <link>https://soidev.tistory.com/35</link>
      <description>&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;REST API (백엔드)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;REST API&lt;/b&gt;&lt;/span&gt;는 백엔드에서 데이터를 처리하고 응답을 JSON으로 주는 방식.&lt;/li&gt;
&lt;li&gt;&lt;span&gt;@RestController&lt;/span&gt;는 반환값이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;&lt;b&gt;html이 아니라 json&lt;/b&gt;&lt;/span&gt;이라는 걸 의미함.&lt;/li&gt;
&lt;li&gt;사용자는 서버에 데이터를 요청하면, 서버는 JSON으로 응답을 돌려줌.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;rarr; 예)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;/api/user/isExistsAccountName?accountName=abc&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;요청하면&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;{ &quot;result&quot;: true }&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 JSON 응답이 옴.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;AJAX (프론트엔드)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;AJAX&lt;/b&gt;&lt;/span&gt;는 HTML 페이지를 새로고침하지 않고, 자바스크립트를 통해 서버에 비동기 요청을 보내는 기술.&lt;/li&gt;
&lt;li&gt;과거엔&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;XMLHttpRequest&lt;/span&gt;를 썼지만, 지금은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;fetch()&lt;/span&gt;로 많이 씀.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;rarr; 즉, 사용자가 아이디를 입력하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;fetch()로 서버에 REST API 요청 &amp;rarr; JSON 응답 받기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 페이지 새로고침 없이 바로 결과 보여줌!&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;  추가 개념 정리: 웹브라우저 역할 &amp;amp; @ResponseBody / @RestController&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;  왜 아이디 중복 확인은 백엔드에서 해야 할까?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;웹브라우저(프론트)는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;HTML, CSS, JavaScript&lt;/b&gt;&lt;span&gt;만 다룸 &amp;rarr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;DB 접근 못 함&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;아이디가 이미 있는지 확인하려면 &amp;rarr; DB에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;SELECT COUNT(*)&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 쿼리 필요&lt;/li&gt;
&lt;li&gt;그래서 **백엔드 서버(Spring)**가 DB를 조회하고 결과를 알려줘야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;즉, 백엔드에 요청(Request)을 보내서 결과(Response)를 받아오는 구조 필요&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&amp;nbsp;@ResponseBody란?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 어노테이션을 쓰면, **함수의 return 값이 HTML이 아니라 데이터(JSON)**로 클라이언트에 응답됨&lt;/li&gt;
&lt;li&gt;보통 컨트롤러 메서드는 View 이름(html 파일 경로)을 리턴하는데,&lt;/li&gt;
&lt;li&gt;&lt;span&gt;@ResponseBody&lt;/span&gt;&lt;span&gt;가 붙으면 &amp;rarr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;return 값이 JSON, 문자열, 숫자 그대로 전송됨&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&amp;nbsp;@RestController란?&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@Controller&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;+&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@ResponseBody&lt;span&gt;를 합친 것&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;이 어노테이션을 클래스에 붙이면 &amp;rarr; 이 안의 모든 메서드는 자동으로 JSON 리턴&lt;/li&gt;
&lt;li&gt;매번&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;@ResponseBody&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;쓰기 귀찮을 때 클래스 단위로 처리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;  그래서 JSON API 만들 땐 대부분&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;@RestController&lt;/span&gt;를 씀&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;근데 리턴타입을 Map&amp;lt;String, Object&amp;gt;으로 많이 사용함&lt;/li&gt;
&lt;li&gt;그 이유 : json은 key-value 구조다 &amp;rarr; Java의&amp;nbsp;Map&amp;lt;String, Object&amp;gt;와 매우 유사함&lt;/li&gt;
&lt;li&gt;이걸 Java에서 간단하게 표현하려면&amp;nbsp;Map이 가장 편하다:&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;그럼 이걸로 뭘 해야 돼?&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;사용자 입력값(아이디)을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;백엔드 함수의 파라미터로 전달&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;백엔드는 이 값을 기반으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;&lt;b&gt;DB에서 중복 여부를 확인&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;확인한 값을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;JSON 형식으로 리턴&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;프론트는 JSON 응답을 받아서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;&lt;b&gt;JS로 사용자한테 메시지 보여줌&lt;/b&gt;&lt;/span&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;&lt;b&gt;회원가입 아이디 중복 체크&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;register.html&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;form&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;registerForm&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;action&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;/user/registerProcess&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;method&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;&amp;lt;!-- 한글이 들어갈 수 있음 --&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; 아이디 : &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;onblur&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;confirmAccountName&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;()&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;inputAccountName&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;accountName&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;onclick&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;confirmAccountName&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;()&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;button&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;아이디 중복확인&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;br&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;accountnameErrorTextBox&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;button추가 중복확인!&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;RestController.java&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;ca&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;finalproject&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;controller&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;java&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;util&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;HashMap&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;java&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;util&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Map&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;org&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;springframework&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;beans&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;factory&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;annotation&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Autowired&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;org&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;springframework&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;web&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;bind&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;annotation&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestMapping&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;org&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;springframework&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;web&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;bind&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;annotation&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestParam&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;org&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;springframework&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;web&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;bind&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;annotation&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RestController&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;ca&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;finalproject&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;service&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UserServiceImpl&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RestController&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//@Controller + @ResponseBody : body는 json이다&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestMapping&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;api/user&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RestUserController&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Autowired&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UserServiceImpl&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;userService&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestMapping&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;isExistsAccountName&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Map&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Object&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;isExistsAccountName&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestParam&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;accountName&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;accountName&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Map&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Object&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;HashMap&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;put&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;userService&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;isExistsAccountName&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;accountName&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;));&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; } &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;@RestController&lt;/span&gt;: 반환값을 JSON 형태로 자동 변환&lt;/li&gt;
&lt;li&gt;@RequestParam(&quot;accountName&quot;)&lt;span&gt;: 쿼리스트링에서 파라미터 받음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Map&amp;lt;String,Object&amp;gt;&lt;span&gt;로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;{ &quot;result&quot;: true/false }&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;형태 반환&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;UserSqlMapper.java&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//회원 존재 여부&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;countuserByAccountName&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;accountName&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;/div&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;UserSqlMapper.xml&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;countuserByAccountName&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; select count(*) from fp_user fu where fu.account_name=#{accountName}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&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;register.html&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;confirmAccountName&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//아이디를 눌렀을때에 그 아이디가 필요한것?&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;inputAccountName&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;document&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getElementById&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;inputAccountName&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// inputAccountName.value; // 이 값ㅂ이 이미 가입한 녀석이냐?&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//restapi를 이제 js로 호출할 것&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//AJAX - js로 링크 변경 없이 페이지 내에서 백엔드에 요청(requeset) 하는 기술&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;fetch&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;`/api/user/isExistsAccountName?accountName=&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;inputAccountName&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;())&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//여기가 reutnr된 값임&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//여기서부터는 그냥 js 일반적인 능력 활용 필요!! 나중엔 여기가 죽어나갈 구간&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;// 백엔드 응답 후 여기부터 실행됨!!(비동기)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;accountnameErrorTextBox&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;document&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getElementById&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;accountnameErrorTextBox&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;result&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;accountnameErrorTextBox&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;color&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;red&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;accountnameErrorTextBox&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;innerText&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;이미 사용중인 아이디입니다.&quot;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;isConfirmedAccountname&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;accountnameErrorTextBox&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;color&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;green&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4fc1ff;&quot;&gt;accountnameErrorTextBox&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;innerText&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;사용 가능한 아이디입니다.&quot;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;isConfirmedAccountname&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; }&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; }) &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; ;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; }&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fetch()&lt;span&gt;로 서버에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;AJAX 방식&lt;/b&gt;&lt;/span&gt;&lt;span&gt;의 요청을 보냄 (&lt;/span&gt;accountName=입력값&lt;span&gt;)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;.then(response =&amp;gt; response.json())&lt;span&gt;: JSON 파싱&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;.then(json =&amp;gt; {...})&lt;/span&gt;: 결과에 따라 텍스트 색과 메시지 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;좋아요기능 구현&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #2f2f2f; color: #000000; text-align: start;&quot;&gt;
&lt;div style=&quot;background-color: #2f2f2f; color: #cccccc;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #669768;&quot;&gt;-- 좋아요T &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #739eca;&quot;&gt;CREATE&lt;/span&gt; &lt;span style=&quot;color: #739eca;&quot;&gt;TABLE&lt;/span&gt; &lt;span style=&quot;color: #9e9e9e;&quot;&gt;fp_like&lt;/span&gt; (&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9e9e9e;&quot;&gt;id&lt;/span&gt; &lt;span style=&quot;color: #c1aa6c;&quot;&gt;INT&lt;/span&gt; &lt;span style=&quot;color: #739eca;&quot;&gt;PRIMARY&lt;/span&gt; &lt;span style=&quot;color: #739eca;&quot;&gt;KEY&lt;/span&gt; &lt;span style=&quot;color: #739eca;&quot;&gt;AUTO_INCREMENT&lt;/span&gt; ,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9e9e9e;&quot;&gt;article_id&lt;/span&gt; &lt;span style=&quot;color: #c1aa6c;&quot;&gt;INT&lt;/span&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9e9e9e;&quot;&gt;user_id&lt;/span&gt; &lt;span style=&quot;color: #c1aa6c;&quot;&gt;INT&lt;/span&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9e9e9e;&quot;&gt;created_at&lt;/span&gt; &lt;span style=&quot;color: #c1aa6c;&quot;&gt;DATETIME&lt;/span&gt; &lt;span style=&quot;color: #739eca;&quot;&gt;DEFAULT&lt;/span&gt; &lt;span style=&quot;color: #c1aa6c;&quot;&gt;NOW&lt;/span&gt;()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;)&lt;span style=&quot;color: #eecc64;&quot;&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2114&quot; data-origin-height=&quot;1380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b39nVQ/btsNSxwnPsh/wms8GXIutO0SablzedDlJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b39nVQ/btsNSxwnPsh/wms8GXIutO0SablzedDlJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b39nVQ/btsNSxwnPsh/wms8GXIutO0SablzedDlJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb39nVQ%2FbtsNSxwnPsh%2Fwms8GXIutO0SablzedDlJk%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;727&quot; height=&quot;475&quot; data-origin-width=&quot;2114&quot; data-origin-height=&quot;1380&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;&lt;b&gt;LikeDto.java&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre id=&quot;code_1747038553316&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.ca.finalproject.dto;

import java.time.LocalDateTime;

import lombok.Data;

@Data
public class LikeDto {
    private int id;
    private int articleId;
    private int userId;
    private LocalDateTime createdAt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BoardSqlMapper.java&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre id=&quot;code_1747038526887&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; //좋이요
    public void createLike(@Param(&quot;articleId&quot;) int articleId,@Param(&quot;userId&quot;) int userId);
    public void deleteLike(@Param(&quot;articleId&quot;) int articleId,@Param(&quot;userId&quot;) int userId);
    public int countByArticleIdAndUserId(@Param(&quot;articleId&quot;) int articleId,@Param(&quot;userId&quot;) int userId);
    public int countByArticleId(@Param(&quot;articleId&quot;) int articleId);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BoardSqlMapper.xml&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747038491767&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- 좋아요 --&amp;gt;
&amp;lt;insert id=&quot;createLike&quot;&amp;gt;
	Insert into fp_like(article_id,user_id) VALUES(#{articleId},#{userId});
&amp;lt;/insert&amp;gt;

&amp;lt;delete id=&quot;deleteLike&quot;&amp;gt;
	delete from fp_like where article_id=#{articleId}, and user_id=#{userId};
&amp;lt;/delete&amp;gt;

&amp;lt;select id=&quot;countByArticleIdAndUserId&quot;&amp;gt;
	select count(*) from fp_like fl where fl.article_id=#{articleId} and fl.user_id=#{userId};
&amp;lt;/select&amp;gt;

&amp;lt;select id=&quot;countByArticleId&quot;&amp;gt;
	select count(*) from fp_like fl where fl.article_id=#{articleId}
&amp;lt;/select&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BoardServiceImpl.java&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre id=&quot;code_1747038476934&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; //좋아요
    public void like(int articleId, int userId){
        boardSqlMapper.createLike(articleId, userId);
    }

    public void unlike(int articleId, int userId){
        boardSqlMapper.deleteLike(articleId, userId);
    }

    public boolean isMyLike(int articleId,int userId){
        return boardSqlMapper.countByArticleIdAndUserId(articleId, userId)&amp;gt;0;
    }

    public int getTotalLikeCount(int articleId){
        return boardSqlMapper.countByArticleId(articleId);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RestBoardController.java&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre id=&quot;code_1747038383501&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.ca.finalproject.board.controller;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.ca.finalproject.board.service.BoardServiceImpl;
import com.ca.finalproject.dto.UserDto;

import jakarta.servlet.http.HttpSession;

@RestController
@RequestMapping(&quot;api/board&quot;)
public class RestBoardController {

    @Autowired
    private BoardServiceImpl boardService;

    @RequestMapping(&quot;like&quot;)
    public Map&amp;lt;String,Object&amp;gt; like(HttpSession session, @RequestParam(&quot;articleId&quot;) int articleId){
        UserDto sessionUserInfo = (UserDto)session.getAttribute(&quot;sessionUserInfo&quot;);
        boardService.like(articleId, sessionUserInfo.getId());

        Map&amp;lt;String,Object&amp;gt; map = new HashMap&amp;lt;&amp;gt;();
        map.put(&quot;result&quot;, &quot;success&quot;);
        return map;
    }

    @RequestMapping(&quot;unlike&quot;)
    public Map&amp;lt;String,Object&amp;gt; unlike(HttpSession session, @RequestParam(&quot;articleId&quot;) int articleId){
        UserDto sessionUserInfo = (UserDto)session.getAttribute(&quot;sessionUserInfo&quot;);
        boardService.unlike(articleId, sessionUserInfo.getId());

        Map&amp;lt;String,Object&amp;gt; map = new HashMap&amp;lt;&amp;gt;();
        map.put(&quot;result&quot;, &quot;success&quot;);
        return map;
    }

    @RequestMapping(&quot;isLiked&quot;)
    public Map&amp;lt;String, Object&amp;gt; isLiked(HttpSession session, @RequestParam(&quot;articleId&quot;) int articleId){
        Map&amp;lt;String, Object&amp;gt; map = new HashMap&amp;lt;&amp;gt;();
        UserDto sessionUserInfo = (UserDto)session.getAttribute(&quot;sessionUserInfo&quot;);
        map.put(&quot;result&quot;, boardService.isMyLike(articleId, sessionUserInfo.getId()));
        return map;
    }

    @RequestMapping(&quot;totalLikeCount&quot;)
    public Map&amp;lt;String,Object&amp;gt; totalLikeCount(@RequestParam(&quot;articleId&quot;) int articleId){     //이경우는 세션 필요없음 로그인 하지 않아도 카운트가 보여야하니까..
        Map&amp;lt;String, Object&amp;gt; map = new HashMap&amp;lt;&amp;gt;();

        map.put(&quot;result&quot;, boardService.getTotalLikeCount(articleId));

        return map;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;readArticle.html&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;script&amp;gt;안에다가 넣기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747038157019&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function like(){
    //로그인 안 했을때
    if(mySessionId==null){ //null인 경우는 로그인 하지않았따
    //alert과 비슷한데 버튼이 두개 나옴
    if(confirm(&quot;로그인 후 이용 가능합니다. 로그인 페이지로 넘어갈래?&quot;)){
    	location.href=&quot;/user/loginPage&quot;;
    }
    return;
	}

    //로그인 했을 때 : like insert하면됨
    fetch(`/api/board/like?articleId=${articleId}`)
        .then(response=&amp;gt; response.json())
        .then(json=&amp;gt;{
            renderHeart();
            renderTotalCount();
    	});
    }

function unlike(){
    //로그인 했을 때 : like insert하면됨
    fetch(`/api/board/unlike?articleId=${articleId}`)
        .then(response=&amp;gt; response.json())
        .then(json=&amp;gt;{
            renderHeart();
            renderTotalCount();
    });
}

function renderTotalCount(){
    fetch(`/api/board/totalLikeCount?articleId=${articleId}`)
        .then(response=&amp;gt;response.json())
        .then(json=&amp;gt;{
            const countBox= document.getElementById(&quot;countBox&quot;);
            countBox.innerText=json.result;
    });
}

function renderHeart(){
            fetch(`/api/board/isLiked?articleId=${articleId}`)
                .then(response=&amp;gt;response.json())
                .then(json=&amp;gt;{
                    const heartBox = document.getElementById(&quot;heartBox&quot;);
                    if(json.result == true){
                        heartBox.classList.add(&quot;bi-heart-fill&quot;);
                        heartBox.classList.remove(&quot;bi-heart&quot;);
                        heartBox.setAttribute(&quot;onclick&quot;,&quot;unlike()&quot;);
                    }else{
                        heartBox.classList.remove(&quot;bi-heart-fill&quot;) ;
                        heartBox.classList.add(&quot;bi-heart&quot;);
                        heartBox.setAttribute(&quot;onclick&quot;,&quot;like()&quot;);
                    }
                });
 }
        

window.addEventListener(&quot;DOMContentLoaded&quot;,()=&amp;gt;{
            //여기가 사실상 시작점
            setMySessionId();
            renderTotalCount();
});&lt;/code&gt;&lt;/pre&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&gt;body에다가 넣기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747038359270&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- 추천 --&amp;gt;
&amp;lt;div&amp;gt;
    &amp;lt;i id=&quot;heartBox&quot; onclick=&quot;like()&quot; class=&quot;bi bi-heart text-danger&quot;&amp;gt;&amp;lt;/i&amp;gt; &amp;lt;span id=&quot;countBox&quot;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>중앙정보기술인재개발원/Spring</category>
      <category>ajax</category>
      <category>js</category>
      <category>restapi</category>
      <category>spring</category>
      <category>자바스크립트</category>
      <author>soidev</author>
      <guid isPermaLink="true">https://soidev.tistory.com/35</guid>
      <comments>https://soidev.tistory.com/35#entry35comment</comments>
      <pubDate>Mon, 12 May 2025 17:55:24 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] final project_2차</title>
      <link>https://soidev.tistory.com/34</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2차&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;카테고리(다중 취미),검색,페이징,파일업로드,암호화, 인터셉터,메일인증, 인터셉터, 자바스크립트 기초&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단일값이 아니라 여러값을 넣을때&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;제1정규화원칙때문에&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6KNw2/btsNLZS8KQP/svcWAuHBZIZRxWZsqkCy0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6KNw2/btsNLZS8KQP/svcWAuHBZIZRxWZsqkCy0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6KNw2/btsNLZS8KQP/svcWAuHBZIZRxWZsqkCy0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6KNw2%2FbtsNLZS8KQP%2FsvcWAuHBZIZRxWZsqkCy0K%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;365&quot; height=&quot;366&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;528&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;checkbox는 똑같은 이름으로 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링크구조로... http의 프로토콜의 정상적인 케이스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터 받을때 Requestparam으로 받고 List로 받으면됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;insert되자마자 primary키가 필요한 경우 : 그때는 쿼리가 두번 실행해야함 mybatis에서 selectKey라는걸 사용함&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2322&quot; data-origin-height=&quot;1056&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djhIvm/btsNLztFoXO/LCqQEDrFt55ZGEbjjzFjk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djhIvm/btsNLztFoXO/LCqQEDrFt55ZGEbjjzFjk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djhIvm/btsNLztFoXO/LCqQEDrFt55ZGEbjjzFjk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjhIvm%2FbtsNLztFoXO%2FLCqQEDrFt55ZGEbjjzFjk1%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;616&quot; height=&quot;280&quot; data-origin-width=&quot;2322&quot; data-origin-height=&quot;1056&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글쓰기할때 파일도 올리기&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;form&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;action&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;/board/writeArticleProcess&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;method&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;enctype&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;multipart/form-data&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; 닉네임 : &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;th:text&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;${session.sessionUserInfo.nickname}&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;br&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; 제목 : &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;br&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; 내용 : &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;br&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;textarea&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;textarea&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;br&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;&amp;lt;!-- 엔터칠수 있는 입력 input 양식 --&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; 이미지 : &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;uploadFiles&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;file&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;multiple&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;accept&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;image/*&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;br&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;&amp;lt;!-- multiple : 이미지 여러개 넣을수있음 accept : image파일만 선택할수만 있음--&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;글 저장&quot;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;form&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestMapping&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;writeArticleProcess&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;) &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;writeArticleProcess&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;HttpSession&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;ModelAttribute&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;ArticleDto&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;params&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;,&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;RequestParam&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;uploadFiles&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;List&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;MultipartFile&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;uploadFiles&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;){ &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//객체를 받을땐 requestparam이 아니라 modelAttribute로 해야함&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;try&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;&amp;nbsp; &amp;nbsp;for&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;MultipartFile&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;multipartFile&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;uploadFiles&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;&amp;nbsp; &amp;nbsp;String&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;filename&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;multipartFile&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getOriginalFilename&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;&amp;nbsp; &amp;nbsp;multipartFile&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;transferTo&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;/Users/soi/uploadFiles/&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;filename&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;));&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&amp;nbsp;} &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Exception&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;) {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;&amp;nbsp; &amp;nbsp; e&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;printStackTrace&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; }&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//userid값이 0임 , session을 가져와야함 &lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UserDto&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;sessionUserDto&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;UserDto&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getAttribute&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;sessionUserInfo&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;params&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;setUserId&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;sessionUserDto&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getId&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;());&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;boardService&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;writeArticle&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;params&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;redirect:/board/mainPage&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//글을 다쓰면 메인페이지로 넘어갈것!&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; }&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uploadFile&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ4Q11/btsNNF8s8og/eTyfWBD3M1M2UWTD939s31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ4Q11/btsNNF8s8og/eTyfWBD3M1M2UWTD939s31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ4Q11/btsNNF8s8og/eTyfWBD3M1M2UWTD939s31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ4Q11%2FbtsNNF8s8og%2FeTyfWBD3M1M2UWTD939s31%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;791&quot; height=&quot;468&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uploadFiles: 서버가 갖고있는 폴더, 클라이언트에선 없는폴더&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;aaa.jpg는 클라이언트에만 갖고있는 파일&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;web과 관련된 코든느 컨트롤러&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;서비스는 웹이 아니여도 호출이 되야함&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;이메일 인증&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 2단계 인증까지 한다음에 여기 들어가서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://myaccount.google.com/security&quot;&gt;https://myaccount.google.com/security&lt;/a&gt;&lt;span style=&quot;background-color: #121212; color: #ececec; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1746750266425&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Google 계정&quot; data-og-description=&quot;&quot; data-og-host=&quot;myaccount.google.com&quot; data-og-source-url=&quot;https://myaccount.google.com/security&quot; data-og-url=&quot;https://myaccount.google.com/intro/security&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://myaccount.google.com/security&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://myaccount.google.com/security&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Google 계정&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;myaccount.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱 비밀번호 만들기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1474&quot; data-origin-height=&quot;1114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9y54d/btsNQgUZtTA/NFobmgH1SAK7uRHcTBay7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9y54d/btsNQgUZtTA/NFobmgH1SAK7uRHcTBay7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9y54d/btsNQgUZtTA/NFobmgH1SAK7uRHcTBay7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9y54d%2FbtsNQgUZtTA%2FNFobmgH1SAK7uRHcTBay7k%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;714&quot; height=&quot;540&quot; data-origin-width=&quot;1474&quot; data-origin-height=&quot;1114&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8fafd; color: #1f1f1f; text-align: start;&quot;&gt;&lt;b&gt;buil.gradle&lt;/b&gt; - &lt;span style=&quot;background-color: #f8fafd; color: #1f1f1f; text-align: start;&quot;&gt;외부 라이브러리 추가&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;implementation&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;'org.springframework.boot:spring-boot-starter-mail'&lt;/span&gt;&lt;/div&gt;
&lt;/div&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;application.properties&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;#메일 세팅&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;spring.mail.host = &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;smtp.gmail.com&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;spring.mail.port = &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;587&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;spring.mail.username = &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;~~~@gmail.com&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;spring.mail.password=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;앱비번&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;spring.mail.properties.mail.smtp.starttls.enable=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;true&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;spring.mail.properties.mail.smtp.starttls.required=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;true&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;spring.mail.properties.mail.smtp.auth=&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;true&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gmail 쓸 것인걸 선언&lt;/li&gt;
&lt;li&gt;구글에서는 587번 포트 제공&lt;/li&gt;
&lt;li&gt;내 이메일 주소 추가&lt;/li&gt;
&lt;li&gt;앱비번 넣기 아래서부터는 메일 마다 다 다를듯?&lt;/li&gt;
&lt;li&gt;암호화하겠다는거&lt;/li&gt;
&lt;li&gt;중간자머시기 암호화기법을 추가하겠다는거?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UserServiceImpl.java&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;일단 먼저 메일 보내보기!&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Autowired&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;JavaMailSender&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;javaMailSender&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;javamailsender 추가 : 얘를 이용해서 메일 보낼수있음(인터페이스임)&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//메일 보내기&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;try&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;MimeMessage&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;mimeMessage&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;javaMailSender&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;createMimeMessage&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;MimeMessageHelper&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;mimeMessageHelper&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;MimeMessageHelper&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;mimeMessage&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;mimeMessageHelper&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;setSubject&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;제목 .. 입니다&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;mimeMessageHelper&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;setText&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;내용 입니다. 야호!!&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;); &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//true를 넣으면 html코드로 날라감&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;mimeMessageHelper&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;setTo&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;userDto&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;getEmail&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;());&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;mimeMessageHelper&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;setFrom&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;aaa@aaa.com&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;FP 관리자&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;); &lt;/span&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//한글이 안들어감 첫번쨰,&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;javaMailSender&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;send&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;mimeMessage&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Exception&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;){&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;printStackTrace&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; }&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;javaMailSender : 실제론 얘가 메일을 보낸 것&lt;/li&gt;
&lt;li&gt;MimeMessage : 메시지 구성요소들(약간 컨테이너? 라고 생각하면될듯)&lt;/li&gt;
&lt;li&gt;근데 mimemessage얘 혼자보내기 껄끄러우서 helper 등장&lt;/li&gt;
&lt;li&gt;try..catch문 쓰는 이유 ?&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bb1WyG/btsNPHeoEKX/kCoZYelUuoDrEuIM9DNGS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bb1WyG/btsNPHeoEKX/kCoZYelUuoDrEuIM9DNGS1/img.png&quot; data-alt=&quot;처음에 회원가입할땐 오류가 뜸&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bb1WyG/btsNPHeoEKX/kCoZYelUuoDrEuIM9DNGS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbb1WyG%2FbtsNPHeoEKX%2FkCoZYelUuoDrEuIM9DNGS1%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;1916&quot; height=&quot;582&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;처음에 회원가입할땐 오류가 뜸&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유 : naver.com로 해야하는데 naver,com으로 해서.. 잘 확인하고 치쟈! 나중엔 console창 띄우게 할것&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMjEVt/btsNTBSODh3/bElbx6pcdTOmayIsGkfNU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMjEVt/btsNTBSODh3/bElbx6pcdTOmayIsGkfNU1/img.png&quot; data-alt=&quot;성공&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMjEVt/btsNTBSODh3/bElbx6pcdTOmayIsGkfNU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMjEVt%2FbtsNTBSODh3%2FbElbx6pcdTOmayIsGkfNU1%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;398&quot; height=&quot;216&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;216&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;성공&lt;/figcaption&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MailAuthDto.java 추가&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;ca&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;finalproject&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;dto&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;java&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;time&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;lombok&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Data&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Data&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;MailAuthDto&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;userId&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;authKey&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;isCompleted&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;completedTime&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;LocalDateTime&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;createdAt&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserSqlMapper.java&lt;/p&gt;
&lt;div style=&quot;background-color: #1f1f1f; color: #cccccc; text-align: start;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #6a9955;&quot;&gt;//메일 인증&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;createMailAuth&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(@&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Param&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;userId&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;userId&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;, @&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;Param&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;authKey&quot;&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;authKey&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #569cd6;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #dcdcaa;&quot;&gt;updateMailAuth&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;authKey&lt;/span&gt;&lt;span style=&quot;color: #cccccc;&quot;&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;/div&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1175&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYaej1/btsNQNMC9ah/Jqkz2jv0NKRNyBA5M0w8pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYaej1/btsNQNMC9ah/Jqkz2jv0NKRNyBA5M0w8pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYaej1/btsNQNMC9ah/Jqkz2jv0NKRNyBA5M0w8pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYaej1%2FbtsNQNMC9ah%2FJqkz2jv0NKRNyBA5M0w8pk%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;719&quot; height=&quot;433&quot; data-origin-width=&quot;1175&quot; data-origin-height=&quot;708&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 컨트롤러에서 정보를 통하게하는데 Filter-&amp;gt;intercetor-&amp;gt;Controlled에 도착&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>중앙정보기술인재개발원/Spring</category>
      <author>soidev</author>
      <guid isPermaLink="true">https://soidev.tistory.com/34</guid>
      <comments>https://soidev.tistory.com/34#entry34comment</comments>
      <pubDate>Mon, 12 May 2025 17:54:57 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] | 세션,쿠키,Session,Cookie</title>
      <link>https://soidev.tistory.com/33</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxkzJK/btsNFh7muhi/crDvOLPgBZMTxr77X3goi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxkzJK/btsNFh7muhi/crDvOLPgBZMTxr77X3goi0/img.png&quot; data-alt=&quot;gpt 출처&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxkzJK/btsNFh7muhi/crDvOLPgBZMTxr77X3goi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxkzJK%2FbtsNFh7muhi%2FcrDvOLPgBZMTxr77X3goi0%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;513&quot; height=&quot;513&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;gpt 출처&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;  세션(Session)이란?&lt;/b&gt;&lt;/span&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; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;세션&lt;/b&gt;은 일반적으로&amp;nbsp;&lt;b&gt;클라이언트가 서버에 접속한 하나의 단위&lt;/b&gt;를 의미함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉,&amp;nbsp;&lt;b&gt;클라이언트와 서버 간의 연결 상태&lt;/b&gt;를 일정 시간 동안 유지하기 위한 수단.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;HTTP는 비연결성(stateless)&lt;/b&gt;&amp;nbsp;프로토콜이기 때문에, 요청이 끝나면 서버는 해당 클라이언트를&amp;nbsp;&lt;b&gt;잊어버림&lt;/b&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그래서 클라이언트를 식별하기 위해&amp;nbsp;&lt;b&gt;세션을 사용&lt;/b&gt;함.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;  세션의 동작 방식&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;클라이언트가 로그인 등의 요청을 보냄&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버는 이 요청에 대해&amp;nbsp;&lt;b&gt;세션 저장 공간(메모리)&lt;/b&gt;&amp;nbsp;을 생성함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이때&amp;nbsp;&lt;b&gt;세션 ID(고유한 값)&lt;/b&gt;&amp;nbsp;를 생성해서&amp;nbsp;&lt;b&gt;클라이언트에게 쿠키로 전달&lt;/b&gt;함.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;쿠키의 역할&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;쿠키는&amp;nbsp;&lt;b&gt;클라이언트(Web Browser) 측에 저장&lt;/b&gt;됨.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;request를 보낼 때마다 자동으로 쿠키를 서버에 전달&lt;/b&gt;함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버는 쿠키에 담긴&amp;nbsp;&lt;b&gt;세션 ID를 보고 어떤 사용자인지 판단&lt;/b&gt;함 &amp;rarr;&amp;nbsp;&lt;b&gt;인증의 핵심!&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;예시 흐름&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클라이언트가 로그인 &amp;rarr;&amp;nbsp;session.setAttribute()로 서버에 사용자 정보 저장&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버는 세션 ID를 클라이언트에게 쿠키로 전달&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이후 요청마다 쿠키에 담긴 세션 ID로 서버는 클라이언트를 식별함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;  쿠키 vs 세션&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;table style=&quot;color: #000000; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;저장 위치&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클라이언트(브라우저)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버(메모리)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;보안&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;낮음 (브라우저에서 접근 가능)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상대적으로 높음&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;생명 주기&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;브라우저가 종료될 때까지 or 설정한 만료 시간까지&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기본 30분 (설정 가능)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;용도&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용자 정보 저장 (간단한 내용)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;로그인 상태 등 서버에 보관할 중요한 정보&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;  세션 관련 특징&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;같은 브라우저 내에서는&amp;nbsp;&lt;b&gt;쿠키를 공유&lt;/b&gt;함 (같은 사용자로 간주됨)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;브라우저를 새로 열면&amp;nbsp;&lt;b&gt;쿠키가 없을 수도 있음 &amp;rarr; 세션도 새로 생성&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;로그아웃하면 서버 메모리에서&amp;nbsp;&lt;b&gt;세션 공간 자체를 제거&lt;/b&gt;함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 쿠키는 클라이언트에 그대로 남아있을 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;세션은 기본적으로&amp;nbsp;&lt;b&gt;30분간 유지&lt;/b&gt;됨 (서버 설정으로 변경 가능)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/di82Ex/btsNDtHUxC7/AcfnoZHhBLGiRBvKRGx4M1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/di82Ex/btsNDtHUxC7/AcfnoZHhBLGiRBvKRGx4M1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/di82Ex/btsNDtHUxC7/AcfnoZHhBLGiRBvKRGx4M1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdi82Ex%2FbtsNDtHUxC7%2FAcfnoZHhBLGiRBvKRGx4M1%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;1206&quot; height=&quot;174&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;로그아웃을 하면 램에 저장했던 것을 아예 그 공간자체를 날려버림&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;근데 쿠키 쪽에선 살아있으 서버쪽에서 이 메모리는 어떻게 되는지 아냐?&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;저 세션은 타임라인이 정해져있음 디폴트가 30분임&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;저 파일명으로 설정시간은 바꿀 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;⚠️ 시스템 규모에 따른 고려사항&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;소규모 또는&amp;nbsp;&lt;b&gt;모노리식(Monolithic)&lt;/b&gt;&amp;nbsp;시스템에서는 세션 방식이 적합함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만&amp;nbsp;&lt;b&gt;중대형 시스템&lt;/b&gt;이나&amp;nbsp;&lt;b&gt;분산 시스템&lt;/b&gt;에서는&amp;nbsp;&lt;b&gt;세션 방식이 부적합할 수 있음&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이유: 세션은&amp;nbsp;&lt;b&gt;서버 메모리에 저장되므로&lt;/b&gt;&amp;nbsp;서버 간 정보 공유가 어려움&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;대안:&amp;nbsp;&lt;b&gt;JWT 토큰 기반 인증 방식&lt;/b&gt;&amp;nbsp;등 사용&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceaMZz/btsNE56qUo1/BYQVtK4F9bLmqU6X8jAzBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceaMZz/btsNE56qUo1/BYQVtK4F9bLmqU6X8jAzBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceaMZz/btsNE56qUo1/BYQVtK4F9bLmqU6X8jAzBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceaMZz%2FbtsNE56qUo1%2FBYQVtK4F9bLmqU6X8jAzBK%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;595&quot; height=&quot;409&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서버쪽에서 session과 관련된 코드가 실행되면 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;loginProcess실행될때, spring에서는 이때 세션의 쿠키가 안 날라오면 새로운 세션 저장공간을 생성함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;id값을 서버 램쪽에다 부여함 ex- value값, 이 세션을 클라이언트에다가 '이걸 쿠키로 저장해'라고 넘김&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이 클라이언트는 저 밸류값 저것을 요청을 할 때마다 여기에 있는 쿠키 값을 무조건 넘기게 되어있음 &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그럴때마다 이것은 인증의 핵심!!! 요청할때마다 클라이언트에 있는 쿠키를 서버에게 전달을 하면 인식을 한다 (session.setAttribute)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;f5은 메인페이지 계속 요청하는 것..&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;541&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buFg7q/btsNE9A3I0y/QT2f3sTAcDwdnLJ1YQLUC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buFg7q/btsNE9A3I0y/QT2f3sTAcDwdnLJ1YQLUC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buFg7q/btsNE9A3I0y/QT2f3sTAcDwdnLJ1YQLUC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuFg7q%2FbtsNE9A3I0y%2FQT2f3sTAcDwdnLJ1YQLUC0%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;654&quot; height=&quot;417&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;541&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;똑같은 웹브라우저가 두세명 있어도 쿠키가 아예 없는 것들도 있음 그럴땐 그냥 널값이 뜬다?&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하나의 웹브라우저는 쿠키를 공유해&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉 계속 새로고침해도&amp;nbsp;http프로토콜은 요청하고 끊어버리는 순간 값을 알 수 없음&lt;/span&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;</description>
      <category>중앙정보기술인재개발원/Spring</category>
      <category>cookie</category>
      <category>request</category>
      <category>session</category>
      <category>spring</category>
      <category>세션</category>
      <category>스프링</category>
      <category>쿠키</category>
      <author>soidev</author>
      <guid isPermaLink="true">https://soidev.tistory.com/33</guid>
      <comments>https://soidev.tistory.com/33#entry33comment</comments>
      <pubDate>Fri, 2 May 2025 10:54:00 +0900</pubDate>
    </item>
  </channel>
</rss>