<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>heegonkim_.log</title>
        <link>https://velog.io/</link>
        <description>❤️</description>
        <lastBuildDate>Mon, 27 Apr 2026 05:55:52 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>heegonkim_.log</title>
            <url>https://velog.velcdn.com/images/heegonkim_/profile/03d5a958-9d66-4481-bda8-dc75acfbcca0/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. heegonkim_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/heegonkim_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[microsoft clarity 사용해보기]]></title>
            <link>https://velog.io/@heegonkim_/microsoft-clarity-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@heegonkim_/microsoft-clarity-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Mon, 27 Apr 2026 05:55:52 GMT</pubDate>
            <description><![CDATA[<p>정말 초초초초 초간단한 아이디어 하나만으로 시작해서 배포까지 했던 정말 간단한 웹사이트가 하나 있다. 
[DueDateTimer.]
(<a href="https://github.com/heegon02/DueDateTimer">https://github.com/heegon02/DueDateTimer</a>)
마감시간을 기준으로 남은 시간을 돌려준다는. 알람과 타이머를 합친 간단한 아이디어 하나로 시작했다. </p>
<p>그렇게 방치하고 버려둔지 약 2년. 
벨로그를 보다가 microsoft clarity 라는 기술을 접하게 되었다. 사용자의 커서 이동, 스크롤, 접속 횟수 등 내가 배포한 웹 서비스에 대해 데이터를 정리해서 보여주는 서비스였다. 가장 좋은 것은 <strong>&#39;영구 완전 무료&#39;</strong>. </p>
<p>너무 신기해서 바로 DueDateTimer 프로젝트를 가져와서 적용해 보았다. </p>
<p>사용법은 정말 간단하다. </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/a460306e-2e73-4348-bbe0-717b1148504f/image.png" alt=""></p>
<p>먼저 microsoft의 Clarity 사이트에 접속한다. </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/4a0304dd-8b7d-4696-934f-bcb34382661d/image.png" alt=""></p>
<p><strong>+새 프로젝트</strong> 버튼을 누르고</p>
<p>성명에는 Clarity에서 사용할 이름을, 
웹 사이트 URL에 배포한 사이트 URL 주소를, 
웹 사이트 업계에는 서비스 분야를 선택해주면 끝이다. </p>
<p>아마 웹 사이트 뿐만 아니라 앱 어플리케이션에도 사용 가능한 것 같다. </p>
<p>그리고 시간이 지나서 데이터가 쌓이면</p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/b317ad87-be8d-4b62-92b5-39c0da99172b/image.png" alt=""></p>
<p>위와 같은 대시보드 창이 출력된다. </p>
<p>나는 첫 대시보드 화면을 접하기까지 약 2시간이 소요된 것 같다. </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/6dfe4121-912d-445f-8bc9-11b0f65b002a/image.png" alt=""></p>
<p>빨간색 동그라미로 표시해놓은 동영상 카메라 같은 부분을 클릭하면 </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/dc96ab49-901c-44c7-95b9-30001757640e/image.png" alt=""></p>
<p>다음과 같은 화면이 보인다. 
맨 왼쪽 재생 버튼을 누르면 사용자의 웹사이트 마우스 커서 이동을 영상으로 만들어서 보여준다. </p>
<p>사용하고 분석해본 결과, 
화면이 회색이 되면 마우스가 해당 웹사이트를 벗어났다는 것 같고
클릭을 많이할수록 마우스 부분이 빨간색으로 표현된다. 
단순 마우스 이동 경로는 주황색으로 표현된다. </p>
<p>오른쪽에는 웹사이트 사용시간, 마우스 클릭 횟수, 페이지 (화면 이동 횟수) 가 간단하게 보여진다. </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/35878afc-9857-4117-90bd-c202f37e20b2/image.png" alt=""></p>
<p>위 이미지와 같이 마우스 이동 경로가 주황색 선으로 표현된다. 그리고 왼쪽에는 세션 인사이트라고 하여 유저의 서비스 이용 내용에 대하여 정리를 해주는 부분이 있다. </p>
<p>새로운 기술을 접하면 일단 무조건 사용을 해보는게 중요해진 시대가 된 것 같다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[컴퓨터비전] 이미지 분류 모델 성능 평가 : 혼동 행렬 (Confusion Matrix)]]></title>
            <link>https://velog.io/@heegonkim_/%EC%9D%B8%EA%B3%B5%EC%A7%80%EB%8A%A5-%EB%B6%84%EB%A5%98-%EB%AA%A8%EB%8D%B8-%EC%84%B1%EB%8A%A5-%ED%8F%89%EA%B0%80-%ED%98%BC%EB%8F%99-%ED%96%89%EB%A0%AC-Confusion-Matrix</link>
            <guid>https://velog.io/@heegonkim_/%EC%9D%B8%EA%B3%B5%EC%A7%80%EB%8A%A5-%EB%B6%84%EB%A5%98-%EB%AA%A8%EB%8D%B8-%EC%84%B1%EB%8A%A5-%ED%8F%89%EA%B0%80-%ED%98%BC%EB%8F%99-%ED%96%89%EB%A0%AC-Confusion-Matrix</guid>
            <pubDate>Thu, 09 Apr 2026 10:44:47 GMT</pubDate>
            <description><![CDATA[<h4 id="이미지-분류-모델에서의-혼동행렬-confusion-matirx">이미지 분류 모델에서의 혼동행렬 (Confusion Matirx)</h4>
<p>분류 모델의 성능을 평가할 때, 4가지의 값을 사용한다. 
TP, FP, FN, TN</p>
<blockquote>
<p>TP =&gt; True + Positive
FP =&gt; False + Positive
FN =&gt; False + Negative
TN =&gt; True + Negative</p>
</blockquote>
<p>먼저 <strong><em>뒷부분(Positive, Negative)은 모델의 예상값</em></strong> 을 의미한다. 
Positive라면, &#39;맞다&#39; 혹은 &#39;있다&#39; 와 같은 긍정의 의미를 지닌다.
반면 Negative라면 &#39;틀리다&#39;, &#39;없다&#39;의 의미가 된다. </p>
<p>반면 <strong><em>앞부분(True, False)은 모델 예상값의 정답 여부</em></strong> 를 의미한다. 
True라면 모델 예상값이 맞다는 것을,
False라면 모델 예상값이 틀렸다는 것을 의미한다. </p>
<p>예를 들어, 이미지에서 사람이 있는지 없는지 찾는 모델이 있다고 가정해보자. 
사람이 있는 곳을 찾고 실제로 해당 이미지에 사람이 있다면, 
P (모델의 예상 : 있다. 즉, Positive)
T (모델 예상 값 정답 여부 : True / 실제로 있기 때문)
=&gt; 합쳐서 TP
이런 식으로 분류 모델의 성능을 평가하게 된다. </p>
<p>모델이 예상값을 틀리는 FP와 FN의 경우 헷갈렸다.
너무 헷갈려서 그림으로 정리해 보았다. </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/422282d8-404f-4d56-bde3-8f9cd0690078/image.jpg" alt="혼동행렬"></p>
<p>따라서 
<strong>FP</strong>는 모델이 이미지에 사람이 있다(Positive)고 예측했는데, False(오답)이기 때문에 실제 이미지에는 사람이 없는 것이다. 
없는데 있다고 예측을 했기 때문에 <strong>&#39;오탐&#39;</strong>이 된다. 
<strong>FN</strong>는 모델이 이미지에 사람이 없다(Negative)고 예측했는데, False(오답)이기 때문에 실제 이미지에는 사람이 있는 것이다. 
있는데 없다고 예측을 했기 때문에 <strong>&#39;미탐&#39;</strong>이 된다. </p>
<h4 id="precision">Precision</h4>
<p>모델이 &#39;있다&#39; 라고 예측한 것 중에서 실제로 맞은 비율이다. 
즉, _P 들 중에서 TP 의 비율을 의미한다. </p>
<p>당연히 수식 또한 Precision = TP / TP + FP 가 된다. 
위 이미지에서 파란색 네모들 중에서 TP 네모의 비율을 생각하면 이해가 쉽다. </p>
<h4 id="recall">Recall</h4>
<p>실제 객체 중 모델이 찾아낸 비율을 의미한다. 
위 이미지에서 사람이 있는 부분에 쳐진 두 개의 네모 (TP, FN) 중에서 TP 네모의 비율을 생각하면 된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OpenCV] 이미지 연산 실습]]></title>
            <link>https://velog.io/@heegonkim_/OpenCV-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%B0%EC%82%B0-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@heegonkim_/OpenCV-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%B0%EC%82%B0-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Tue, 31 Mar 2026 05:45:18 GMT</pubDate>
            <description><![CDATA[<h3 id="이미지-업로드-및-이미지-정보-확인하기">이미지 업로드 및 이미지 정보 확인하기</h3>
<p><code>cv2.imread()</code> -&gt; 이미지를 디스크에서 읽어들여 OpenCV의 NumPy 배열 형태로 로드한다. </p>
<p>*<em><code>image.shape</code> *</em></p>
<p><code>image.shape[0]</code> -&gt; Height
<code>image.shape[1]</code> -&gt; Width
<code>image.shape[2]</code> -&gt; Channels</p>
<p><code>image.size</code> -&gt; 전체 픽셀 수(Height x Width x Channels)를 의미한다. </p>
<p><code>image[y, x]</code> -&gt; (y,x) 위치의 픽셀(BGR) 정보를 배열 형태로 얻는다. 
=&gt; image[Height, Width]</p>
<p><em>OpenCV에서는 BGR (Blue, Green, Red) 순서가 기본이다. (보통은 RGB)</em></p>
<p><em>image[y, x][0] =&gt; Blue</em>
<em>image[y, x][1] =&gt; Green</em>
<em>image[y, x][2] =&gt; Red</em></p>
<h4 id="코드로-공부하기">코드로 공부하기</h4>
<pre><code># Colab에 이미지 업로드
uploaded = files.upload()

# 업로드된 첫 번째 이미지 파일 이름 가져오기
filename = list(uploaded.keys())[0]

# 이미지를 BGR 컬러로 읽기
image = cv2.imread(filename, cv2.IMREAD_COLOR)

# 이미지 기본 정보 확인하기
print(&quot;이미지 크기 확인 (Height, Width, Channels) : &quot;, image.shape)
print(&quot;이미지 전체 픽셀 수 : &quot;, image.size)

# 특정 픽셀의 BGR 값 확인
# (100,100) 위치로 가정
y, x = 100, 100 

if y &lt; image.shape[0] and x &lt; image.shape[1]:
    px = image[y, x]
    print(f&quot;({y},{x}) 위치 픽셀의 BGR 값 : &quot;, px)
    print(f&quot;({y},{x}) 위치 픽셀의 Blue 값 : &quot;, px[0])
    print(f&quot;({y},{x}) 위치 픽셀의 Green 값 : &quot;, px[1])
    print(f&quot;({y},{x}) 위치 픽셀의 Red 값 : &quot;, px[2])    
else:
    print(f&quot;이미지 크기가 작아서, {y},{x} 픽셀이 존재하지 않습니다.&quot;)</code></pre><h3 id="특정-범위-픽셀-변경하기">특정 범위 픽셀 변경하기</h3>
<h4 id="슬라이싱-방식">슬라이싱 방식</h4>
<pre><code># 이미지 복사본 생성
image_copy = image.copy()

start_time = time.time()
rows = min(100, image_copy.shape[0])
cols = min(100, image_copy.shape[1])

# 슬라이싱을 이용한 특정 범위 변경
image_copy2 = image.copy()
start_time = time.time()
image_copy2[0:rows, 0:cols] = [255,255,255] # 검정색으로 변경
end_time = time.time()
print(&quot;%.6f seconds&quot; %(end_time - start_time))

# 결과 이미지 보기
cv2_imshow(image_copy2)</code></pre><h4 id="roi-region-of-interest-추출-및-복사">ROI (Region of Interest) 추출 및 복사</h4>
<h4 id="roi-추출">ROI 추출</h4>
<pre><code># ROI 추출 예시
image_copy3 = image.copy()

# 안전하게 min() 함수를 사용하여 이미지 범위 내에서만 추출
roi = image_copy3[20:min(150, image_copy3.shape[0]), 70:min(200, image_copy3.shape[1])]

# 잘라낸 ROI 확인
cv2_imshow(roi)</code></pre><h4 id="roi-복사-후-다른-곳에-붙여넣기">ROI 복사 후 다른 곳에 붙여넣기</h4>
<pre><code># ROI를 다른 위치에 복사

# 이미지의 높이 정보를 가져와서 h_roi에 저장하고, 너비 정보를 w_roi 에 저장
h_roi, w_roi = roi.shape[:2]

# 이미지 끝 지점 지정
end_y = min(h_roi, image_copy3.shape[0])
end_x = min(w_roi, image_copy3.shape[1])

# 0부터 end_y까지, 0부터 end_x 까지를 roi 로 변경
image_copy3[0:end_y, 0:end_x] = roi

cv2_imshow(image_copy3)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[OpenCV] OpenCV 시작하기]]></title>
            <link>https://velog.io/@heegonkim_/OpenCV</link>
            <guid>https://velog.io/@heegonkim_/OpenCV</guid>
            <pubDate>Mon, 30 Mar 2026 01:35:06 GMT</pubDate>
            <description><![CDATA[<h3 id="opencv-설치">OpenCV 설치</h3>
<p><code>!pip install opencv-python-headless</code></p>
<h3 id="opencv-라이브러리-임포트">OpenCV 라이브러리 임포트</h3>
<pre><code>import cv2
from google.colab.patches import cv2_imshow</code></pre><p>=&gt; colab에서 이미지를 표시하기 위해 cv2_imshow 사용</p>
<h3 id="opencv-이미지-읽어오기-cv2imread">OpenCV 이미지 읽어오기 (cv2.imread)</h3>
<p>예시 코드)</p>
<pre><code>img1 = &#39;cat.jpg&#39;
img_color = cv2.imread(img1, cv2.IMREAD_COLOR)</code></pre><p><strong>cv2.imread(이미지 파일명, 색상 옵션)</strong></p>
<p>색상 옵션
cv2.IMREAD_COLOR -&gt; 컬러 이미지로 읽기
cv2.IMREAD_GRAYSCALE -&gt; 흑백 이미지로 읽기
cv2.IMREAD_UNCHANGED -&gt; 알파 채널까지 그대로 읽기</p>
<h3 id="opencv-이미지-저장하기-cv2imwrite">OpenCV 이미지 저장하기 (cv2.imwrite)</h3>
<p>예시 코드 ) </p>
<pre><code>cv2.imwrite(&#39;saved_image.jpg&#39;, img_color)</code></pre><p>=&gt; cv2.imread()를 통해 읽어온 이미지를 img_color 라는 변수에 담았다가, &#39;saved_image.jpg&#39;라는 파일명으로 저장.</p>
<p><strong>cv2.imwrite(저장할 파일명, cv2.imread()로 읽어온 이미지 변수명)</strong></p>
<h3 id="opencv-이미지-색상-변환하기-cv2cvtcolor">OpenCV 이미지 색상 변환하기 (cv2.cvtColor)</h3>
<p>예시 코드 )</p>
<pre><code>img_gray = cv2.cvtColor(img_color, cv2.COLOR_BGR2GRAY)
cv2_imshow(img_gray)
cv2.imwrite(&#39;gray_image.jpg&#39;, img_gray)</code></pre><p><strong>cv2.cvtColor(cv2.imread()로 읽어온 이미지 변수명, 색상변환옵션)</strong></p>
<p>색상 변환 옵션
cv2.COLOR_BGR2GRAY -&gt; 컬러를 흑백으로
cv2.COLOR_BGR2HSV -&gt; 컬러를 HSV로</p>
<h3 id="opencv-이미지-크기-조정-cv2resize">OpenCV 이미지 크기 조정 (cv2.resize)</h3>
<p>예시 코드 )</p>
<pre><code>h, w = img_color.shape[:2]
img_resized = cv2.resize(img_color, (w//2, h//2)</code></pre><p>OpenCV에서 이미지는 NumPy 배열 형태이다. 
(높이, 너비, 채널) 순서로 구성된 tuple 을 반환한다. 
여기에서는 [:2]를 통해서 앞의 두 개 값(높이, 너비)만 가져오겠다는 뜻이다. </p>
<p>이미지 크기 변환을 위해 OpenCV에서는 cv2.resize(너비,높이) 순서로 값을 입력받는다. </p>
<h3 id="opencv-이미지-블러---가우시안-블러">OpenCV 이미지 블러 - 가우시안 블러</h3>
<p>예시 코드 )</p>
<pre><code>cv2.GaussianBlur(이미지파일명, (7,7), 0)</code></pre><p>(7,7)은 커널 사이즈, 0은 자동 계산되는 표준 편차</p>
<p><strong>cv2.GaussianBlur(cv2.imread()로 읽어온 이미지 변수명, 커널사이즈, 0)</strong></p>
<h3 id="opencv-이미지-블러---미디언-블러">OpenCV 이미지 블러 - 미디언 블러</h3>
<p>예시 코드 )</p>
<pre><code>cv2.medianBlur(이미지파일명, 7)</code></pre><p><strong>cv2.medianBlur(cv2.imread()로 읽어온 이미지 변수명, 커널사이즈)</strong></p>
<h3 id="opencv-이미지-크기-확인하기">OpenCV 이미지 크기 확인하기</h3>
<p><code>cv2.imread()로 읽어온 이미지 변수명.shape</code></p>
<h3 id="opencv-전체-픽셀-수-확인하기">OpenCV 전체 픽셀 수 확인하기</h3>
<p><code>cv2.imread()로 읽어온 이미지 변수명.size</code></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] java.time 패키지를 이용한 시간 관련 세팅]]></title>
            <link>https://velog.io/@heegonkim_/%EC%9E%90%EB%B0%94-java.time-%ED%8C%A8%ED%82%A4%EC%A7%80%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%8B%9C%EA%B0%84-%EA%B4%80%EB%A0%A8-%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@heegonkim_/%EC%9E%90%EB%B0%94-java.time-%ED%8C%A8%ED%82%A4%EC%A7%80%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%8B%9C%EA%B0%84-%EA%B4%80%EB%A0%A8-%EC%84%B8%ED%8C%85</guid>
            <pubDate>Wed, 07 Jan 2026 07:21:56 GMT</pubDate>
            <description><![CDATA[<p>Java 시간 관련 타입을 정리해보았다. 
보통 백엔드로 스프링 혹은 스프링부트를 사용하기 때문에 자바의 시간 타입을 아는 것은 백엔드 구현에서 Entity를 세팅할 때 매우 중요하다. 
** <code>java.time</code> 패키지 ** 에 관한 설명이다. </p>
<hr>
<h3 id="localdatetime">LocalDateTime</h3>
<p><strong>import문</strong></p>
<pre><code>import java.time.LocalDateTime;</code></pre><p><strong>데이터 형태</strong>
<code>2026-01-07T13:30:50</code></p>
<p>&#39;날짜 + 시간&#39; 데이터를 담는 형태로, 가장 많이 사용한다. 
타임존은 없다. </p>
<p>MySQL DB를 기준으로 DB 테이블에서는 DATETIME 속성을 사용한다. 
자바 LocalDateTime 과 1:1로 매칭된다. 
PostgreSQL DB를 기준으로는 timestamp without time zone 속성을 사용한다. </p>
<hr>
<h3 id="offsetdatetime">OffsetDateTime</h3>
<p><strong>import문</strong></p>
<pre><code>import java.time.OffsetDateTime;</code></pre><p><strong>데이터 형태</strong>
<code>2026-01-07T13:30:50+09:00</code>
<em>날짜 + 시간 + UTC 기준 오프셋</em> 데이터를 담는다. 
T를 기준으로 앞은 날짜, 뒤는 시간 그리고 +/- 뒤는 UTC를 기준으로 몇 시간 차이 나는 시간의 데이터라고 저장했다 라는 정보를 담는다. </p>
<p>한국의 경우, UTC 보다 +9시간이기 때문에 위 예시처럼 +09:00 라고 담는다. 만약 다른 국가에서도 해당 서비스를 이용해야 한다면, -09:00 시간을 해서 UTC 시간으로 맞춰주고 사용자가 선택한 국가에 맞게 +- 로 국가 시간에 맞춰서 데이터를 보여주면 된다. </p>
<hr>
<h3 id="localdate--localtime">LocalDate / LocalTime</h3>
<p>LocalDate 는 날짜만 담고, LocalTime 은 시간만 담는다. </p>
<p><strong>import문</strong></p>
<pre><code>import java.time.LocalDate;
import java.time.LocalTime;</code></pre><p><strong>데이터 형태</strong>
<code>2026-01-07</code>
<code>13:35:40</code></p>
<hr>
<h3 id="사용-예시">사용 예시</h3>
<p>DB에 TIMESTAMP started_at 과 TIMESTAMP ended_at 이라는 속성이 있다. </p>
<p>백엔드 Entity 폴더에서는</p>
<pre><code>import jakarta.persistence.*;

@Entity
@Getter
@Setter
@Table(name= &quot;테이블명&quot;) {
    @Column(name = &quot;started_at&quot;)
    private LocalDateTime startedAt;

    @Column(name = &quot;ended_at&quot;)
    private LocalDateTime endedAt;
}</code></pre><p>다음과 같이 Entity를 선언해주면 된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[chatGPT 유료 워크스페이스 사용하다가 종료 되었을 때 다시 개인 계정으로 복구시키는 방법]]></title>
            <link>https://velog.io/@heegonkim_/chatGPT-%EC%9C%A0%EB%A3%8C-%EC%9B%8C%ED%81%AC%EC%8A%A4%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8B%A4%EA%B0%80-%EC%A2%85%EB%A3%8C-%EB%90%98%EC%97%88%EC%9D%84-%EB%95%8C-%EB%8B%A4%EC%8B%9C-%EA%B0%9C%EC%9D%B8-%EA%B3%84%EC%A0%95%EC%9C%BC%EB%A1%9C-%EB%B3%B5%EA%B5%AC%EC%8B%9C%ED%82%A4%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@heegonkim_/chatGPT-%EC%9C%A0%EB%A3%8C-%EC%9B%8C%ED%81%AC%EC%8A%A4%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8B%A4%EA%B0%80-%EC%A2%85%EB%A3%8C-%EB%90%98%EC%97%88%EC%9D%84-%EB%95%8C-%EB%8B%A4%EC%8B%9C-%EA%B0%9C%EC%9D%B8-%EA%B3%84%EC%A0%95%EC%9C%BC%EB%A1%9C-%EB%B3%B5%EA%B5%AC%EC%8B%9C%ED%82%A4%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 22 Dec 2025 01:05:26 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/heegonkim_/post/08d05ca0-861b-4f83-9e3a-7fe0d7645d11/image.png" alt=""></p>
<p>chatGPT를 워크스페이스 형태로 유료로 사용하게 되면, 유료 사용 종료 이후에 gpt 첫 화면에서 위와 같은 화면을 마주하게 된다. </p>
<p>다시 &#39;개인 계정&#39; 워크스페이스로 전환을 해야 하는데, 어떤 것도 클릭이 불가하다. 그냥 단순한 뷰페이지 저게 다다. </p>
<p>방법이 하나 있다. </p>
<p>시크릿창을 열고, 똑같이 chatGPT를 들어간다. 아마 로그인되어 있지 않을 것이다. 동일한 계정의 로그인을 진행하고 유료 사용이 종료된 워크스페이스를 나가면 된다. 이후에 다시 원래 gpt 페이지로 돌아와서 새로고침(F5)을 해보면 원래 내 개인 계정 워크스페이스로 돌아온 것을 확인할 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[인공지능] 3학년 2학기 인공지능 기말고사 정리]]></title>
            <link>https://velog.io/@heegonkim_/%EC%9D%B8%EA%B3%B5%EC%A7%80%EB%8A%A5-3%ED%95%99%EB%85%84-2%ED%95%99%EA%B8%B0-%EC%9D%B8%EA%B3%B5%EC%A7%80%EB%8A%A5-%EA%B8%B0%EB%A7%90%EA%B3%A0%EC%82%AC-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@heegonkim_/%EC%9D%B8%EA%B3%B5%EC%A7%80%EB%8A%A5-3%ED%95%99%EB%85%84-2%ED%95%99%EA%B8%B0-%EC%9D%B8%EA%B3%B5%EC%A7%80%EB%8A%A5-%EA%B8%B0%EB%A7%90%EA%B3%A0%EC%82%AC-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 17 Dec 2025 03:05:29 GMT</pubDate>
            <description><![CDATA[<h2 id="10장-선형회귀">10장. 선형회귀</h2>
<ul>
<li><p>회귀 : 데이터들을 2차원 공간에 찍은 후에 이들 데이터들을 가장 잘 설명하는 직선이나 곡선을 찾는 것. </p>
</li>
<li><p>선형회귀 : 입력 데이터를 가장 잘 설명하는 기울기와 절편값을 찾는 무제. 
선형회귀 기본식 -&gt; f(x) = Wx + b
여기에서 기울기 W는 가중치 (weight)
절편 b는 바이어스 (bias)</p>
</li>
<li><p>단순선형회귀 : <strong>입력 변수(x)가 하나</strong>일 때, 직선으로 데이터를 설명하는 회귀.
f(x) = Wx + b</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/f4969d42-7b8a-43d7-bf2d-99202bdd19bb/image.png" alt="단순선형회귀"></p>
<p>위 이미지와 같이 공부시간(입력변수 x 하나)에 따라 일정하게 출력값(y) 가 달라지는 경우에 단순선형회귀를 사용하면 된다. </p>
<ul>
<li>다중 선형 회귀 : 입력 변수(x, y, z, ...) 가 여러 개일 때, 직선(평면)으로 데이터를 설명하는 회귀. 
쉽게 말해 입력 변수가 여러 개이고, 입력 변수마다 가중치를 다르게 주는 방법이다. </li>
</ul>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/0916b33a-a24b-438a-987e-410f68b0770a/image.png" alt="다중선형회귀"></p>
<ul>
<li>손실 함수 : 예측이 얼마나 틀렸는지를 수치화하여 나타낸 것. 
필요한 이유 : 회귀의 목표는 데이터를 가장 잘 설명하는 직선을 찾는 것인데, 이 직선이 잘 맞는지를 판단할 기준이 손실 함수이다. 
기본식 : (예측값 - 정답값)을 제곱해서 모두 더해서 평균을 낸다. </li>
</ul>
<p>많이 사용하는 손실 함수 (MSE)</p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/950811d2-7404-4260-a15e-83a87f734830/image.png" alt="MSE"></p>
<p>어떤 w와 b를 넣어봤을 때 손실값이 최대한 작은 것을 구하는 것이 우리의 목표. 
그런데 w, b가 어떤 값이 최소 손실값이 나올지 한 번에 알 수 없다. 
이때 사용하는 것이 경사하강법. </p>
<ul>
<li>경사하강법 : 손실을 줄이는 방향으로 w와 b를 조금씩 고쳐가는 방법. 
목표 : argmin Loss(W, b)
경사하강법은 손실 함수를 최소화하기 위해 손실이 감소하는 방향으로 가중치와 바이어스를 반복적으로 조금씩 업데이트 하는 방법. </li>
</ul>
<hr>
<h2 id="11장-knn">11장. KNN</h2>
<ul>
<li>KNN
개념 : 새로운 데이터가 주어지면 기존 데이터들과의 거리를 계산한 뒤에 가장 가까운 K개의 이웃을 선택하고, 분류 문제의 경우 다수결로, 회귀 문제의 경우 평균값으로 예측한다. </li>
</ul>
<p>분류 : 신규 데이터를 기존 그룹 중 어디에 속할지 판단하는 것. </p>
<p>KNN 장단점
장점 : 구현이 간단하고 학습 과정이 없다. 
단점 : 데이터가 많아지면 느리다. 거리 계산 비용이 크다. </p>
<ul>
<li>Decision Tree
개념 : 스무고개 게임 (계속 질문을 던져서 데이터를 분류(예측)하는 모델. </li>
</ul>
<p>위에서 아래로 내려가면서 판단.
Root node : 처음 질문. 
Leaf node : 최종 결정. </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/e3220529-52d5-48cf-8a4d-a24fd26597c7/image.png" alt="Decision Tree"></p>
<p>Decision Tree 기본 파라미터. 
criterion : 노드를 분기할 때 사용하는 기준. 
max_depth : 트리의 최대 깊이. 
min_samples_split : 노드를 나누기 위한 최소 데이터 개수. (데이터가 너무 적을 때 쪼개지 않도록 막는다. )
min_samples_leaf : 리프 노드에 남아야 하는 최소 데이터 수.</p>
<p>Decision Tree 장점 : 데이터를 분류하는 논리의 흐름을 시각적으로 볼 수 있다. 
코드의 이해가 쉽다. 
Decision Tree 단점 : 과적합에 빠지기 쉽다. </p>
<p>KNN : 거리 기반
Decision Tree : 질문 기반</p>
<hr>
<h2 id="12장-퍼셉트론">12장. 퍼셉트론</h2>
<ul>
<li>퍼셉트론
개념 : 여러 입력을 받아 가중합을 계산한 뒤, 활성화 함수를 거쳐
출력을 내는 가장 기본적인 신경망. 
퍼셉트론 구조 : 
z = w1x1 + w2x2 + … + wnxn + b
가중합 ⇒ z
입력 ⇒ x1, x2, x3, …, xn
퍼셉트론은 하나의 뉴런만을 사용한다. 
다수의 입력을 받아서 하나의 신호를 출력하는 장치이다. </li>
</ul>
<hr>
<h2 id="13장-다층-퍼셉트론">13장. 다층 퍼셉트론</h2>
<ul>
<li><p>다층 퍼셉트론
개념 : 입력층과 출력층 사이에 은닉층을 가지고 있는 신경망. 
퍼셉트론에서는 계단함수를 활성화 함수로 사용했지만, 
다층 퍼셉트론 (MLP)에서는 다양한 비선형 함수들을 활성화 함수로 사용한다. 
MLP에서 쓰는 활성화 함수 ⇒ Sigmoid, TanH, ReLU</p>
</li>
<li><p>역전파 알고리즘
출력에서 생긴 오차를 뒤쪽에서 앞쪽으로 전달하면서, 
각 가중치를 얼마나 고쳐야 하는지 계산하는 방법. </p>
</li>
</ul>
<p>가중치를 조절하는 과정
가중치를 증가시켰을 때 오차 감소 → 가중치 증가. 
가중치를 증가시켰을 때 오차 증가 → 가중치 감소. </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/1d05ca0f-5925-446b-8f60-4aea142f0ac6/image.png" alt="역전파"></p>
<p>미분값 &lt; 0 (미분값이 음수라면)
오른쪽으로 갈수록 계속 손실 감소. 
w 증가시켜야 한다. </p>
<p>미분값 &gt; 0 (미분값이 양수라면)
오른쪽으로 갈수록 계속 손실 증가. 
w 감소시켜야 한다. </p>
<p>미분값이 음수라면,  w 가중치를 증가시키고. 
미분값이 양수라면, w 가중치를 감소시켜야.
손실값을 줄일 수 있다. </p>
<hr>
<h2 id="14장-딥러닝">14장. 딥러닝</h2>
<p>DNN (딥러닝)은 MLP(다층 퍼셉트론) 에서 은닉층의 개수를 증가시킨 것이다. </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/2e727aa9-6849-4d45-a67f-613b9c2a9e19/image.png" alt="딥러닝"></p>
<p>Gradient 소멸 문제
개념 : 은닉층이 많아질수록 역전파되는 기울기가 점점 작아져서
앞쪽 층의 가중치가 거의 학습되지 않는 현상. 
Gradient 소멸 발생 이유 : 시그모이드, 탄젠트 함수와 같은 활성화 함수의 작은 미분값이 
역전파 과정에서 반복적으로 곱해지기 때문이다. </p>
<p>Gradient 폭발 문제
개념 : 역전파 중 기울기가 너무 커져
가중치가 발산하는 현상. </p>
<p>역전파 과정에서 실제로 전달되는 것은 ‘기울기’
w ← w - 학습률 x 기울기
기울기가 클 경우 많이 고치고, 
기울기가 작을 경우 거의 안 고치고, 
기울기가 0 이면 학습을 멈춘다. </p>
<p>역전파는 뒤에서 앞으로 기울기를 전달한다. 
그리고 전달할 때마다 곱해진다. </p>
<p>은닉층이 많아질 때, 은닉층마다 기울기를 곱하니까 기울기값이 작다면 0에 수렴해진다. </p>
<p>가중치 초기화의 필요성
역전파에서 오차는 가중치가 곱해져서 전달되는데, 
모든 가중치가 0이면 오차 역전파가 제대로 되지 않는다. </p>
<p>그래서 가중치 초기화는 그래디언트 소멸과 폭발을 방지하고, 
신경망의 안정적인 학습을 위해 필요하다. 
Dropout (과적합 방지)의 효과랑 기능 파악하기. 
Dropout은 일부 뉴런을 랜덤하게 비활성화하여, 과적합을 방지하고, 모델의 일반화 성능을 향상시킨다. </p>
<hr>
<h2 id="15장-cnn">15장. CNN</h2>
<p><strong>CNN</strong> (컨벌루션 신경망) 개념 : 
CNN은 이미지를 한 번에 보지 않고 작은 영역씩 보면서 특징을 뽑고, 
중요한 정보만 남겨서 마지막에 분류하는 신경망이다. </p>
<p>CNN의 순서
(컨벌루션 → Pooling → 컨벌루션 → Pooling) → Flatten → Dense → 출력</p>
<p><strong>컨벌루션</strong>
작은 필터(커널)가 이미지를 슬라이싱하면서 특정 패턴(엣지, 모서리 등)을 찾는다. 
그러니까 작은 필터로 이미지 전체를 훑으면서 계산하는 연산이다. 
필터 : 패턴을 검사하는 기준표. (패턴이 얼마나 강한지 계산) 
필터가 많아질수록, 더 다양한 특징을 본다. 
그 연산 결과를 특징맵이라고 한다. 
필터가 1개라면 특징맵도 1개만 반환. 
하지만 16개라면 특징맵도 16개 반환된다. 
패턴을 검사하는 기준표이기 때문이다. </p>
<p><strong>Feature Map</strong> ⇒ 컨벌루션 연산의 결과로 만들어진 ‘특징만 남은 이미지’</p>
<p><strong>Pooling 연산</strong>
목적 : 특징맵의 크기를 줄여, 계산량과 과적합을 줄이기 위해서 사용. 
폴링 종류 : Max Pooling, Average Pooling. 
Max Pooling (영역에서 최댓값), Average Pooling (영역에서 평균값)</p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/00d438a3-3c95-432d-9a48-a9d1a7dab822/image.png" alt="CNN"></p>
<p><strong>Flatten</strong>
컨벌루션 결과는 2D 아니면 3D 데이터이다. 
하지만 Dense 는 1D 벡터 데이터만 입력 가능하다. 
그래서 Flatten 과정을 거쳐서 펴줘야 한다. 
한줄 정의 : 특징맵을 1차원 벡터로 변환하는 연산.
Dense 레이어는 최종 분류를 수행하는 층이다. </p>
<hr>
<h2 id="16장-영상인식">16장. 영상인식</h2>
<p>왜 픽셀값을 255로 나눌까?
⇒ 픽셀값은 원래 0에서 255 사이의 값을 가진다. 
그런데 이 값 그대로 가중치에 곱하면, 가중합 z 값의 숫자가 너무 커진다. 
이 값이 그대로 활성화 함수로 들어가면 활성화 함수가 너무 커져 포화 구간에 들어가고, 
이로 인해 미분값이 0에 가까워져 Gradient 소멸이 발생한다. 
그래서 255로 나눠서 0~1 사이의 작은 값으로 바꿔준다. </p>
<p>데이터 증대
: 한정된 데이터에서 여러 가지로 변형된 데이터를 만들어내는 기법. 
이미지를 회전, 이동, 반전하여 과적합을 방지하고 일반화 성능을 향상시킨다. </p>
<p>ImageDataGenerator
: 이미지 데이터를 자동으로 전처리 + 증강해주는 도구.</p>
<p>rescale=1./255 ⇒ 픽셀값 정규화. 
rotation_range ⇒ 살짝 회전. 
width_shift_range ⇒ 좌우 이동. 
height_shift_range ⇒ 상하 이동. 
zoom_range ⇒ 확대/축소.
horizontal_flip ⇒ 좌우 반전. </p>
<p>전이학습
: 이미 학습된 모델을 가져와 새 문제에 재사용하는 것. 
장점 : 적은 데이터로도 가능하고 학습이 빠르고, 성능이 좋다. 
단점 : 기존 데이터와 너무 다르면 성능 저하. 
전이 3단계</p>
<ol>
<li>사전 학습된 모델 불러오기 (VGG, ResNet, MobileNet 등)</li>
<li>기존 레이어 동결</li>
<li>새로운 Dense 레이어 학습. (새 문제의 클래스에 맞게 새롭게 판단)</li>
</ol>
<hr>
<h2 id="17장-강화학습">17장. 강화학습</h2>
<ul>
<li><p>강화학습
개념 : 정답을 주지 않고, 행동의 결과로 받은 ‘보상’을 기준으로
어떤 행동이 좋은지 스스로 배우는 학습 방법. </p>
</li>
<li><p>강화학습 구성 요소
Agent → 강화 학습의 중심이 되는 객체
Environment → 에이전트가 작동하는 물리적 세계
State → 에이전트의 현재 상황 (미로 게임이라면 에이전트의 위치가 된다)
Action → 에이전트의 행동
Reward → 보상 (환경으로부터의 피드백)</p>
</li>
</ul>
<p>Q테이블과 Q 함수
Q(s, a)는 상태 s에서 행동 a를 했을 때 앞으로 받을 ‘기대 보상’
Q테이블
행은 상태, 열은 행동, 값은 Q값. 
처음에는 전부 값이 0이다. 우연히 목표에 도착하면 값이 채워지기 시작한다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CRUD]]></title>
            <link>https://velog.io/@heegonkim_/CRUD</link>
            <guid>https://velog.io/@heegonkim_/CRUD</guid>
            <pubDate>Fri, 12 Dec 2025 05:09:35 GMT</pubDate>
            <description><![CDATA[<h2 id="crud">CRUD</h2>
<ul>
<li>개념 : DB에서 데이터를 관리하는 4가지 기본 연산. </li>
<li>C : Create (생성)
R : Read (조회)
U : Update (수정)
D : Delete (삭제)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[RESTful API]]></title>
            <link>https://velog.io/@heegonkim_/RESTful-API</link>
            <guid>https://velog.io/@heegonkim_/RESTful-API</guid>
            <pubDate>Fri, 12 Dec 2025 05:07:09 GMT</pubDate>
            <description><![CDATA[<h2 id="restful">RESTful</h2>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/f14d4380-8d0d-40ab-ab9d-8ac66aedc17b/image.png" alt="REST API 구조 이미지"></p>
<ul>
<li><p>개념 : URL 로 식별하고, HTTP 메서드로 행위를 표현하는 API 설계 규칙이자, 아키텍처 스타일이다. 
쉽게 정리해보면 데이터베이스의 테이블을 URL 로 노출하고, SQL 쿼리문을 HTTP Method 로 바꾸고. 결과 반환은 JSON 혹은 XML 로 반환해주는 형태이다. </p>
</li>
<li><p>URL : 자원 위치 (DB)</p>
</li>
<li><p>HTTP Method : 행위 (SQL 쿼리)</p>
</li>
<li><p>HTTP Method
GET : 조회
POST : 등록
PUT : 수정
DELETE : 삭제</p>
</li>
</ul>
<p>이미지 출처 : <a href="https://velog.io/@somday/RESTful-API-%EC%9D%B4%EB%9E%80">https://velog.io/@somday/RESTful-API-%EC%9D%B4%EB%9E%80</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring MVC Project 열었는데 home.jsp 파일에서 JSTL 에러 발생하는 경우 대응 방법]]></title>
            <link>https://velog.io/@heegonkim_/Spring-MVC-Project-%EC%97%B4%EC%97%88%EB%8A%94%EB%8D%B0-home.jsp-%ED%8C%8C%EC%9D%BC%EC%97%90%EC%84%9C-JSTL-%EC%97%90%EB%9F%AC-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EA%B2%BD%EC%9A%B0-%EB%8C%80%EC%9D%91-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@heegonkim_/Spring-MVC-Project-%EC%97%B4%EC%97%88%EB%8A%94%EB%8D%B0-home.jsp-%ED%8C%8C%EC%9D%BC%EC%97%90%EC%84%9C-JSTL-%EC%97%90%EB%9F%AC-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EA%B2%BD%EC%9A%B0-%EB%8C%80%EC%9D%91-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Sat, 22 Nov 2025 08:24:11 GMT</pubDate>
            <description><![CDATA[<ol>
<li>프로젝트 우클릭 -&gt; Maven -&gt; Update Project</li>
<li>Force Update of Sanpshots/Release 체크 후 OK </li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[JSP 뷰 페이지 한글 깨지는 경우 해결 방법]]></title>
            <link>https://velog.io/@heegonkim_/JSP-%EB%B7%B0-%ED%8E%98%EC%9D%B4%EC%A7%80-%ED%95%9C%EA%B8%80-%EA%B9%A8%EC%A7%80%EB%8A%94-%EA%B2%BD%EC%9A%B0-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@heegonkim_/JSP-%EB%B7%B0-%ED%8E%98%EC%9D%B4%EC%A7%80-%ED%95%9C%EA%B8%80-%EA%B9%A8%EC%A7%80%EB%8A%94-%EA%B2%BD%EC%9A%B0-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Sat, 22 Nov 2025 08:20:39 GMT</pubDate>
            <description><![CDATA[<p>JSP로 웹뷰페이지를 띄우려는데 한글이 이상하게 깨지는 경우가 있다. 
이럴 경우 2가지 방법으로 대응이 가능하다. </p>
<h3 id="첫-번째-방법">첫 번째 방법.</h3>
<p>.jsp 파일마다 설정하는 방법이 있다. 
jsp 파일의 맨 위에 아래 코드를 넣어주면 된다. 
하지만 해당 jsp 뷰페이지만 한글이 깨지지 않는다. </p>
<p><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot; %&gt;</code></p>
<hr>
<h3 id="두-번째-방법">두 번째 방법.</h3>
<p>두 번째 방법은 web.xml 파일에 넣는 방법이다. 
이렇게 하면 아예 모든 jps (view page)의 한글이 깨지지 않게 된다. </p>
<pre><code>    &lt;filter&gt;
        &lt;filter-name&gt;encodingFilter&lt;/filter-name&gt;
        &lt;filter-class&gt;org.springframework.web.filter.CharacterEncodingFilter
        &lt;/filter-class&gt;
        &lt;init-param&gt;
            &lt;param-name&gt;encoding&lt;/param-name&gt;
            &lt;param-value&gt;UTF-8&lt;/param-value&gt;
        &lt;/init-param&gt;
        &lt;init-param&gt;
            &lt;param-name&gt;forceEncoding&lt;/param-name&gt;
            &lt;param-value&gt;true&lt;/param-value&gt;
        &lt;/init-param&gt;
    &lt;/filter&gt;

    &lt;filter-mapping&gt;
        &lt;filter-name&gt;encodingFilter&lt;/filter-name&gt;
        &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
    &lt;/filter-mapping&gt;```
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 초기 설정]]></title>
            <link>https://velog.io/@heegonkim_/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%B4%88%EA%B8%B0-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@heegonkim_/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%B4%88%EA%B8%B0-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Sat, 22 Nov 2025 08:19:43 GMT</pubDate>
            <description><![CDATA[<h2 id="spring-설치-하는-법-스프링-stsexe">Spring 설치 하는 법 (스프링, STS.exe)</h2>
<p>✔️ 스프링 프레임워크의 설치는 javaJDK 설치가 완벽하게 끝나고, Path 경로까지 지정한 후에 진행해야 한다. </p>
<p>JDK 는 11 버전을, Path 경로는 아래와 같이 진행하였다. 
<a href="https://velog.io/@heegonkim_/Java-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95">jdk 설치하고, path 경로 지정하는 방법</a></p>
<p>한 마디로 이클립스 위에서 자바 클래스가 온전하게 실행되는 상태에서 진행한다고 생각하면 편하다.</p>
<p>스프링의 경우 Spring 3.0.0 버전과 Spring 4.0.0 버전이 있는데, 스프링 프레임워크를 사용하기 위해서는 3점대 버전을 사용해야 한다. 4점대 버전부터는 스프링 &#39;부트&#39;이기 때문이다. </p>
<p>먼저, 스프링 3점대 버전을 구해서 설치해주어야 한다. 설치는 어렵지 않다. </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/6732e553-5541-498f-8773-929072b270c4/image.png" alt="프로젝트생성"></p>
<p>문제는 설치를 마친 이후에, 처음 STS.exe 를 실행해주었을 때 프로젝트 생성을 위해 Spring Legacy Project를 선택해주는데 </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/18e26d31-e8c9-434d-9acb-652f6ae3ebc6/image.png" alt="프로젝트생성2"></p>
<p>처음에는 위 이미지와 달리, Spring MVC Project 라는 선택지가 없다. </p>
<p>✔️ Spring MVC Project 프로젝트를 생성하기 위해서는 아래와 같이 진행하면 된다. </p>
<p><code>작업 폴더 위치</code> 아래에 <code>.metadata</code> 이 아래에  <code>.plugins</code>이 아래에 있는 <code>org.springsource.ide.eclipse.commons.content.core</code> 폴더를 찾아야 한다. </p>
<p>이 안에 <code>https-content.xml</code> 파일을 넣어주어야 한다. 
https-context.xml 파일은 웹서핑을 통해서 어렵지 않게 구할 수 있다. </p>
<p>✔️ 여기까지 완료가 되면, STS.exe를 완전히 종료한 이후에 다시 실행하여 프로젝트를 생성하면 위 이미지와 같이 &#39;Spring MVC Project&#39; 선택지가 생긴 것을 확인할 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/74f4e5e1-24dd-4db7-ad53-dfa7833f1dc8/image.png" alt=""></p>
<p>next 버튼을 누르게 되면 다음과 같은 화면이 나오는데, 도메인을 지정해주는 설정 사항이다. 반드시 <code>단어1.단어2.단어3</code> 와 같이, 3개의 구조로 이뤄주어야 한다. </p>
<p>예를 들면, <code>com.mycompany.myapp</code>, <code>kr.blog.velog</code> 와 같다. </p>
<p>✔️ 여기까지 진행했으면 이 이후에 Tomcat 서버를 스프링 위에 얹어주면 된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 JDK 설치 및 환경변수 설정]]></title>
            <link>https://velog.io/@heegonkim_/%EC%9E%90%EB%B0%94-JDK-%EC%84%A4%EC%B9%98-%EB%B0%8F-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@heegonkim_/%EC%9E%90%EB%B0%94-JDK-%EC%84%A4%EC%B9%98-%EB%B0%8F-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Sat, 22 Nov 2025 08:17:49 GMT</pubDate>
            <description><![CDATA[<h3 id="자바-개발-환경을-만드는-순서">자바 개발 환경을 만드는 순서</h3>
<ol>
<li>JDK 설치 (구글에 jdk download 검색하면 oracle 다운로드 사이트가 나온다. 이곳에서 진행하면 된다. )</li>
<li>내PC 시스템 환경변수 설정</li>
<li>cmd에서 <code>javac</code>, <code>java -version</code> 로 jdk 설치 잘 되었는지 확인하기. </li>
<li>자바 편집기인 eclipse 다운로드 및 설치</li>
</ol>
<hr>
<h3 id="java-시스템-환경변수-설정">Java 시스템 환경변수 설정</h3>
<p>JDK를 설치한 이후에 진행할 수 있다. </p>
<p>내PC -&gt; 속성 -&gt; 고급 시스템 설정 -&gt; 환경 변수에서 설정.</p>
<p>사용자 변수와 시스템 변수. 두 가지가 존재한다. 
시스템 변수에서 변수와 값을 할당해주면, 어떤 사용자든 Java를 이용할 수 있다. 
그냥 시스템 변수로 세팅하는게 무난하다. 
<img src="https://velog.velcdn.com/images/heegonkim_/post/1de4b59d-028a-4d75-8ba7-44245cf87183/image.jpg" alt=""></p>
<ol>
<li><p>JAVA_HOME</p>
<pre><code>c:\Program Files\Java\jdk-21</code></pre><p>(자바 JDK가 설치된 폴더 위치 - 보통 다음과 같다.)</p>
</li>
<li><p>CLASSPATH</p>
<pre><code>.;</code></pre></li>
<li><p>Path (이미 기존에 존해하는 시스템 혹은 사용자 변수명일 것이다. 편집을 눌러서 해당 경로를 추가하고, 맨 위로 올려주면 된다.)</p>
<pre><code>%JAVA_HOME%\bin;</code></pre></li>
</ol>
<p>시스템 환경변수 설정 이유 =&gt; 운영체제 어디에서든 자바를 인식하기 위해서.
Path 환경 변수에 Java JDK의 bin 폴더 경로를 등록함으로써, java.exe, javac.exe 라는 Java 실행 파일을 찾아 실행하게 하기 위해서이다. </p>
<hr>
<h3 id="cmd에서-jdk-정상적으로-설치되었는지-확인하는-명령어">cmd에서 jdk 정상적으로 설치되었는지 확인하는 명령어</h3>
<p>시스템 환경변수 설정 이후에는 cmd에 들어가서
<code>javac</code> 와 <code>java -version</code> 명령어를 실행해줘야 한다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 스프링 MVC 웹 프로젝트 : 게시판 만들기 (Heegon's Board)]]></title>
            <link>https://velog.io/@heegonkim_/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-MVC-%EC%9B%B9-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B2%8C%EC%8B%9C%ED%8C%90-%EB%A7%8C%EB%93%A4%EA%B8%B0-Heegons-Board</link>
            <guid>https://velog.io/@heegonkim_/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-MVC-%EC%9B%B9-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B2%8C%EC%8B%9C%ED%8C%90-%EB%A7%8C%EB%93%A4%EA%B8%B0-Heegons-Board</guid>
            <pubDate>Fri, 21 Nov 2025 12:57:08 GMT</pubDate>
            <description><![CDATA[<h2 id="📌-개요">📌 개요</h2>
<ul>
<li><p><a href="https://github.com/heegon02/SpringMVCBoardStudy">GitHub</a></p>
</li>
<li><p>[시연영상 링크]
(<a href="https://youtu.be/bwIsmcJjieo?si=cxLkADZhO2yxvDpY">https://youtu.be/bwIsmcJjieo?si=cxLkADZhO2yxvDpY</a>)</p>
</li>
<li><p><strong>구현 기능</strong>
회원가입, 로그인, 로그아웃
개인정보수정
새 글 작성, 글 수정, 글 삭제
댓글 작성</p>
</li>
<li><p><strong>기술 스택</strong>
언어 : Java
Framework : Spring (Spring MVC Project)
DB : MySQL
CSS : Bootstrap</p>
</li>
</ul>
<h2 id="📌-db--table">📌 DB : table</h2>
<p><strong>user</strong>
<img src="https://velog.velcdn.com/images/heegonkim_/post/90873ab8-31d8-4efa-ad4e-f33ff0309da5/image.png" alt="user테이블"></p>
<p><strong>board</strong>
<img src="https://velog.velcdn.com/images/heegonkim_/post/2077551c-1341-4300-878e-3b675f0d635c/image.png" alt="board테이블"></p>
<h2 id="📌-코드-흐름도">📌 코드 흐름도</h2>
<p><strong>회원가입 뷰페이지 이동</strong>
<img src="https://velog.velcdn.com/images/heegonkim_/post/191f1d52-b5cd-45ba-afdf-e610776326e7/image.jpg" alt="회원가입"></p>
<p><strong>회원가입 실제 로직 처리</strong>
<img src="https://velog.velcdn.com/images/heegonkim_/post/e5d6371a-57f2-4309-a064-ab285ec43369/image.jpg" alt="회원가입 로직"></p>
<p>다른 페이지들도 흐름은 동일하다. 
url 주소값에 따라서 컨트롤러에서 분기를 해주고, 단순히 바로 뷰페이지를 띄우면 되는 경우에는 return &quot;뷰페이지&quot;.
DB와의 연계가 필요한 작업에 경우에는 아래 이미지와 같이 DB 작업을 처리하는 Command 인터페이스 구현 자바 클래스를 통해서
Dao 에 있는 DB 연계 메소드를 거치는 방식으로 구성하였다. </p>
<h2 id="📌-화면-ui">📌 화면 UI</h2>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/1775ba87-38b9-47a5-bfbf-26454466b759/image.png" alt="로그인"></p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/cc163e15-4815-40ec-a118-6f9b15dda8d3/image.png" alt="메인페이지"></p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/ac458c04-f02b-4218-9477-22b067ba6892/image.png" alt="글 작성"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 스프링으로 게시판 기능 구현하기-1차 (회원가입, 로그인, 로그아웃)]]></title>
            <link>https://velog.io/@heegonkim_/Spring-%EC%8A%A4%ED%94%84%EB%A7%81%EC%9C%BC%EB%A1%9C-%EA%B2%8C%EC%8B%9C%ED%8C%90-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-1%EC%B0%A8-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83</link>
            <guid>https://velog.io/@heegonkim_/Spring-%EC%8A%A4%ED%94%84%EB%A7%81%EC%9C%BC%EB%A1%9C-%EA%B2%8C%EC%8B%9C%ED%8C%90-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-1%EC%B0%A8-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83</guid>
            <pubDate>Thu, 20 Nov 2025 04:57:16 GMT</pubDate>
            <description><![CDATA[<h2 id="구현-기능">구현 기능</h2>
<ul>
<li>회원가입</li>
<li>로그인</li>
<li>로그아웃</li>
</ul>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/60b6d371-6a3d-4b63-a348-76887ea70c1a/image.png" alt="로그인"></p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/a281480c-1227-4fc1-ae7f-b083be05f466/image.png" alt="회원가입"></p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/626218fc-eacb-4f0a-93b6-6524c08a546e/image.png" alt="메인페이지"></p>
<hr>
<h2 id="db-mysql">DB (MySQL)</h2>
<h3 id="member-테이블">member 테이블</h3>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/2736f59a-3242-4d01-a4d1-585456f1e195/image.png" alt="DB구조"></p>
<hr>
<h2 id="웹-프로젝트-폴더구조">웹 프로젝트 폴더구조</h2>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/1272d6ba-a5b2-4cca-ada3-4ddd7fa2901f/image.png" alt="폴더구조"></p>
<hr>
<h2 id="code">Code</h2>
<h3 id="dto">Dto</h3>
<p><strong>Dto.java</strong></p>
<ul>
<li>DB의 자료형을 그대로 담은 빈즈(데이터 클래스)</li>
<li>DB에서 문자열 자료형은 <em>String</em> 타입으로 필드값을 선언해주고, birth 와 같이 date(날짜) 자료형은 <em>Timestamp</em> 타입으로 필드를 선언해주었다. timestamp 타입은 <code>import java.sql.Timestamp;</code> 로 import 해주어야 사용 가능하다. </li>
</ul>
<pre><code>package kr.study.spring.dto;

import java.sql.Timestamp;

public class Dto {

    private String userId;
    private String password;
    private String email;
    private String name;
    private String residentNumber;
    private String address;
    private String detailAddress;
    private Timestamp birth;
    private String interest;
    private String introduce;

    public Dto() {}
    public Dto(String userId, String password, String email, String name, String residentNumber, 
            String address, String detailAddress, Timestamp birth, String interest, String introduce) {
        this.userId = userId;
        this.password = password;
        this.email = email;
        this.name = name;
        this.residentNumber = residentNumber;
        this.address = address;
        this.detailAddress = detailAddress;
        this.birth = birth;
        this.interest = interest;
        this.introduce = introduce;
    }

    public String getUserId() {
        return userId;
    }
    public void setUserId(String userId) {
        this.userId = userId;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getResidentNumber() {
        return residentNumber;
    }
    public void setResidentNumber(String residentNumber) {
        this.residentNumber = residentNumber;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public String getDetailAddress() {
        return detailAddress;
    }
    public void setDetailAddress(String detailAddress) {
        this.detailAddress = detailAddress;
    }
    public Timestamp getBirth() {
        return birth;
    }
    public void setBirth(Timestamp birth) {
        this.birth = birth;
    }
    public String getInterest() {
        return interest;
    }
    public void setInterest(String interest) {
        this.interest = interest;
    }
    public String getIntroduce() {
        return introduce;
    }
    public void setIntroduce(String introduce) {
        this.introduce = introduce;
    }
}
</code></pre><h3 id="dao">Dao</h3>
<p><strong>Dao.java</strong></p>
<ul>
<li>실제 DB에 사용하는 쿼리문 <pre><code>package kr.study.spring.dao;
</code></pre></li>
</ul>
<p>import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;</p>
<p>import javax.sql.DataSource;</p>
<p>import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;</p>
<p>public class Dao {</p>
<pre><code>private DataSource dataSource;

public Dao() {
    try {
        String configLocation = &quot;classpath:application.xml&quot;;
        AbstractApplicationContext ctx
        = new GenericXmlApplicationContext(configLocation);

        this.dataSource = ctx.getBean(&quot;dataSource&quot;, DataSource.class);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public void joinAction(String userId, String password, String email, String name,
                       String residentNumber, String address, String detailAddress,
                       Date birth, String interest, String introduce) {

    Connection conn = null;
    PreparedStatement ps = null;

    String sql = &quot;INSERT INTO member (userId, password, email, name, residentNumber, address, detailAddress, birth, interest, introduce) &quot; +
                 &quot;VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)&quot;;

    try {
        conn = dataSource.getConnection();
        ps = conn.prepareStatement(sql);

        ps.setString(1, userId);
        ps.setString(2, password);
        ps.setString(3, email);
        ps.setString(4, name);
        ps.setString(5, residentNumber);
        ps.setString(6, address);
        ps.setString(7, detailAddress);
        ps.setDate(8, birth);
        ps.setString(9, interest);
        ps.setString(10, introduce);

        ps.executeUpdate();

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (ps != null) ps.close();
            if (conn != null) conn.close();
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }
}

public boolean loginAction(String userId, String password) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;

    String sql = &quot;select * from member where userId = ? and password = ?&quot;;

    try {
        conn = dataSource.getConnection();
        ps = conn.prepareStatement(sql);

        ps.setString(1, userId);
        ps.setString(2, password);

        rs = ps.executeQuery();

        if (rs.next()) {
            return true;
        } else {
            return false;
        }

    } catch(Exception e) {
        e.printStackTrace();
        return false;
    } finally {
        try {
            if (rs != null) rs.close();
            if (ps != null) ps.close();
            if (conn != null) conn.close();
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }
}</code></pre><p>}</p>
<pre><code>
### Controller

**HomeController.java**</code></pre><p>package kr.study.spring.controller;</p>
<p>import java.util.Map;</p>
<p>import javax.servlet.http.HttpServletRequest;</p>
<p>import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;</p>
<p>import kr.study.spring.command.Command;
import kr.study.spring.command.JoinAction;
import kr.study.spring.command.LoginAction;</p>
<p>@Controller
public class HomeController {</p>
<pre><code>Command command = null;

//회원가입 페이지
@RequestMapping(&quot;/join&quot;)
public String join() {
    return &quot;join&quot;;
}

//회원가입 처리 페이지
@RequestMapping(value=&quot;/joinAction&quot;, method=RequestMethod.POST)
public String joinAction(HttpServletRequest request, Model model) {
    model.addAttribute(&quot;request&quot;, request);
    command = new JoinAction();
    command.execute(model);
    return &quot;redirect:login&quot;;
}

//로그인 페이지
@RequestMapping(&quot;/login&quot;)
public String login() {
    return &quot;login&quot;;
}

//로그인 처리 페이지
@RequestMapping(value=&quot;/loginAction&quot;, method=RequestMethod.POST)
public String loginAction(HttpServletRequest request, Model model) {
    model.addAttribute(&quot;request&quot;, request);
    command = new LoginAction();
    command.execute(model);

    Map&lt;String, Object&gt; map = model.asMap();
    Boolean loginResult = (Boolean) map.get(&quot;loginResult&quot;);

    if (loginResult != null &amp;&amp; loginResult) {
        return &quot;redirect:main&quot;;
    } else {
        return &quot;login&quot;;
    }

}

//메인 페이지
@RequestMapping(&quot;/main&quot;)
public String main(HttpServletRequest request) {
    if (request.getSession().getAttribute(&quot;userId&quot;) == null) {
        return &quot;redirect:login&quot;;
    }
    return &quot;main&quot;;
}

//로그아웃 처리 페이지
@RequestMapping(&quot;/logout&quot;)
public String logout(HttpServletRequest request) {
    request.getSession().invalidate();
    return &quot;redirect:login&quot;;
}

//개인정보 수정 화면 페이지
//@RequestMapping(&quot;/editProfile&quot;)
//public String editProfile(HttpServletRequest request, Model model) {
    //구현 예정.
//}

//개인정보 실제 수정 로직 페이지</code></pre><p>}</p>
<pre><code>
### View 페이지

**login.jsp**</code></pre><p>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;</p>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인 페이지입니다</title>
</head>
<body style="text-align:center">
<h1>Login</h1>
<form method="post" action="loginAction" name="Loginform">
    <table cellspacing="0" style="width:100px;  height:200px; margin:0 auto;">
        <tr>
            <td>id </td>
            <td><pre>     </pre></td>
            <td><input type="text" name="userId"></td>
        </tr>
        <tr>
            <td>Password </td>
            <td><pre>     </pre></td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td colspan="3">
                <input type="submit" value="로그인">
                <input type="button" value="회원가입" onclick="location.href='join'">
            </td>
        </tr>
    </table>
</form>
</body>
</html>
```

<p><strong>join.jsp</strong></p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;회원가입 페이지&lt;/title&gt;
    &lt;script src=&quot;https://t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js&quot;&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body style=&quot;text-align:center;&quot;&gt;
&lt;form method=&quot;post&quot; action=&quot;joinAction&quot; id=&quot;joinForm&quot;&gt;
&lt;table align=&quot;center&quot; border=&quot;1&quot; cellspacing=&quot;0&quot; width=&quot;650&quot;&gt;

        &lt;tr align=&quot;center&quot;&gt;
            &lt;td colspan=&quot;2&quot; bgcolor=&quot;lightgray&quot;&gt;
                회원 기본 정보
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td align=&quot;center&quot; bgcolor=&quot;#f0f0ef&quot;&gt;아이디:&lt;/td&gt;
            &lt;td align=&quot;left&quot;&gt;&lt;input type=&quot;text&quot; name=&quot;userId&quot; id=&quot;userId&quot;&gt;4~12자의 영문 대소문자와 숫자로만 입력&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td align=&quot;center&quot; bgcolor=&quot;#f0f0ef&quot;&gt;비밀번호:&lt;/td&gt;
            &lt;td align=&quot;left&quot;&gt;&lt;input type=&quot;password&quot; name=&quot;password&quot; id=&quot;password&quot;&gt;4~12자의 영문 대소문자와 숫자로만 입력&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td align=&quot;center&quot; bgcolor=&quot;#f0f0ef&quot;&gt;비밀번호확인:&lt;/td&gt;
            &lt;td align=&quot;left&quot;&gt;&lt;input type=&quot;password&quot; name=&quot;passwordCheck&quot; id=&quot;passwordCheck&quot;&gt;4~12자의 영문 대소문자와 숫자로만 입력&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td align=&quot;center&quot; bgcolor=&quot;#f0f0ef&quot;&gt;메일주소:&lt;/td&gt;
            &lt;td align=&quot;left&quot;&gt;&lt;input type=&quot;text&quot; name=&quot;email&quot; id=&quot;email&quot;&gt;예)id@domain.com&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td align=&quot;center&quot; bgcolor=&quot;#f0f0ef&quot;&gt;이름:&lt;/td&gt;
            &lt;td align=&quot;left&quot;&gt;&lt;input type=&quot;text&quot; name=&quot;name&quot; id=&quot;name&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr align=&quot;center&quot;&gt;
            &lt;td colspan=&quot;2&quot; bgcolor=&quot;lightgray&quot;&gt;개인 신상 정보&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td bgcolor=&quot;#f0f0ef&quot;&gt;주민등록번호:&lt;/td&gt;
            &lt;td align=&quot;left&quot;&gt;&lt;input type=&quot;password&quot; name=&quot;residentNumber&quot; id=&quot;residentNumber&quot;&gt;예)1234561234567&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td bgcolor=&quot;#f0f0ef&quot;&gt;생일:&lt;/td&gt;
            &lt;td align=&quot;left&quot;&gt;&lt;input type=&quot;text&quot; size = &quot;5&quot; name=&quot;year&quot; id=&quot;year&quot;&gt;년
                &lt;select id=&quot;month&quot; name=&quot;month&quot;&gt;
                    &lt;option&gt;1&lt;/option&gt;
                    &lt;option&gt;2&lt;/option&gt;
                    &lt;option&gt;3&lt;/option&gt;
                    &lt;option&gt;4&lt;/option&gt;
                    &lt;option&gt;5&lt;/option&gt;
                    &lt;option&gt;6&lt;/option&gt;
                    &lt;option&gt;7&lt;/option&gt;
                    &lt;option&gt;8&lt;/option&gt;
                    &lt;option&gt;9&lt;/option&gt;
                    &lt;option&gt;10&lt;/option&gt;
                    &lt;option&gt;11&lt;/option&gt;
                    &lt;option&gt;12&lt;/option&gt;
                &lt;/select&gt;
                월
                &lt;select id =&quot;day&quot; name=&quot;day&quot;&gt;
                    &lt;option&gt;1&lt;/option&gt;
                    &lt;option&gt;2&lt;/option&gt;
                    &lt;option&gt;3&lt;/option&gt;
                    &lt;option&gt;4&lt;/option&gt;
                    &lt;option&gt;5&lt;/option&gt;
                    &lt;option&gt;6&lt;/option&gt;
                    &lt;option&gt;7&lt;/option&gt;
                    &lt;option&gt;8&lt;/option&gt;
                    &lt;option&gt;9&lt;/option&gt;
                    &lt;option&gt;10&lt;/option&gt;
                    &lt;option&gt;11&lt;/option&gt;
                    &lt;option&gt;12&lt;/option&gt;
                    &lt;option&gt;13&lt;/option&gt;
                    &lt;option&gt;14&lt;/option&gt;
                    &lt;option&gt;15&lt;/option&gt;
                    &lt;option&gt;16&lt;/option&gt;
                    &lt;option&gt;17&lt;/option&gt;
                    &lt;option&gt;18&lt;/option&gt;
                    &lt;option&gt;19&lt;/option&gt;
                    &lt;option&gt;20&lt;/option&gt;
                    &lt;option&gt;21&lt;/option&gt;
                    &lt;option&gt;22&lt;/option&gt;
                    &lt;option&gt;23&lt;/option&gt;
                    &lt;option&gt;24&lt;/option&gt;
                    &lt;option&gt;25&lt;/option&gt;
                    &lt;option&gt;26&lt;/option&gt;
                    &lt;option&gt;27&lt;/option&gt;
                    &lt;option&gt;28&lt;/option&gt;
                    &lt;option&gt;29&lt;/option&gt;
                    &lt;option&gt;30&lt;/option&gt;
                    &lt;option&gt;31&lt;/option&gt;
                &lt;/select&gt;
                일
                &lt;a style=&quot;font-size:small;&quot;&gt;:  주민등록번호를 입력하면 자동입력됩니다.&lt;/a&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td align=&quot;center&quot; bgcolor=&quot;#f0f0ef&quot;&gt;주소:&lt;/td&gt;
            &lt;td align=&quot;left&quot;&gt;
                &lt;input type=&quot;text&quot; name=&quot;address&quot; id= &quot;address&quot; size=&quot;30&quot; placeholder=&quot;주소 검색 버튼 클릭&quot; readonly&gt;
                &lt;input type=&quot;button&quot; onclick=&quot;findAddress()&quot; value=&quot;주소 검색&quot;&gt; &lt;br&gt;
                &lt;input type=&quot;text&quot; name=&quot;detailAddress&quot; id = &quot;detailAddress&quot; placeholder=&quot;상세 주소 입력&quot;&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td align=&quot;center&quot; bgcolor=&quot;#f0f0ef&quot;&gt;관심분야:&lt;/td&gt;
            &lt;td align=&quot;left&quot; id=&quot;interest&quot; name=&quot;interest&quot;&gt;
                &lt;input type=&quot;checkbox&quot; name=&quot;interest&quot; value=&quot;컴퓨터&quot;&gt;컴퓨터
                &lt;input type=&quot;checkbox&quot; name=&quot;interest&quot; value=&quot;인터넷&quot;&gt;인터넷
                &lt;input type=&quot;checkbox&quot; name=&quot;interest&quot; value=&quot;여행&quot;&gt;여행
                &lt;input type=&quot;checkbox&quot; name=&quot;interest&quot; value=&quot;영화감상&quot;&gt;영화감상
                &lt;input type=&quot;checkbox&quot; name=&quot;interest&quot; value=&quot;음악감상&quot;&gt;음악감상
            &lt;/td&gt;
        &lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot; bgcolor=&quot;#f0f0ef&quot;&gt;자기소개:&lt;/td&gt;
            &lt;td align=&quot;left&quot;&gt;
                &lt;textarea cols=&quot;50&quot; rows=&quot;5&quot; name=&quot;introduce&quot; id=&quot;introduce&quot;&gt;
                &lt;/textarea&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
&lt;/table&gt;

&lt;button type=&quot;submit&quot; style=&quot;margin-top:15px;&quot;&gt;회원 가입&lt;/button&gt;
&lt;button type=&quot;reset&quot; style=&quot;margin-top:15px;&quot;&gt;다시 입력&lt;/button&gt;
&lt;/form&gt;

&lt;script src=&quot;${pageContext.request.contextPath}/resources/join.js&quot;&gt;&lt;/script&gt;



&lt;/body&gt;


&lt;/html&gt;</code></pre><p><strong>webapp/resources/join.js</strong></p>
<pre><code>//id 유효성검사
function checkId() {

    var userId = document.getElementById(&quot;userId&quot;).value.trim();
    const idRegex = /^[a-zA-Z0-9]{4,12}$/;

    if(!idRegex.test(userId)) {
        alert(&quot;아이디 입력 형식 오류&quot;);
        return false;
    }
    return true;
}    

//password 유효성검사
function checkPassword() {
    var password = document.getElementById(&quot;password&quot;).value.trim();
    var userId = document.getElementById(&quot;userId&quot;).value.trim();
    const pwRegex = /^[a-zA-Z0-9]{4,12}$/;

    if (!pwRegex.test(password)) {
        alert(&quot;비밀번호는 4~12자의 영문 대소문자와 숫자로만 입력해야 합니다.&quot;);
        return false;
    }

    if (password === userId) {
        alert(&quot;아이디와 비밀번호는 같을 수 없습니다.&quot;);
        return false;
    }
    return true;
}


//password확인 유효성검사
function checkPassword2() {

    var password = document.getElementById(&quot;password&quot;).value.trim();
    var password2 = document.getElementById(&quot;passwordCheck&quot;).value.trim();

    if (password2 !== password) {
        alert(&quot;비밀번호와 비밀번호확인이 서로 다릅니다.&quot;);
        return false;
    }
    return true;
}

//메일주소 유효성검사
function checkEmail() {

    var email = document.getElementById(&quot;email&quot;).value.trim();
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

    if(!emailRegex.test(email)) {
        alert(&quot;메일 주소 형식이 정해져 있습니다. &quot;);
        return false;
    }
    return true;
}

//주민등록번호 유효성 검사 및 생일 자동 입력
function checkResidentNum() {

    var residentNumber = document.getElementById(&quot;residentNumber&quot;).value.trim();
    const pwRegex = /^[0-9]{13}$/;

    if(!pwRegex.test(residentNumber)) {
        alert(&quot;주민등록번호는 13자리를 입력해야 합니다.&quot;);
        return false;
    }

    let sum = 0;
    const multipliers = [2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5];

    for (let i = 0; i&lt;12; i++) {
        sum += parseInt(residentNumber.charAt(i)) * multipliers[i];
    }

    const calculatedVerifyNum = (11 - (sum % 11)) % 10;

    if(calculatedVerifyNum !== parseInt(residentNumber.charAt(12))) {
        alert(&quot;유효하지 않은 주민등록번호입니다.&quot;);
        return false;
    }

    //주민등록번호에서 생년월일 추출
    var yearPrefix = (residentNumber.charAt(7) === &quot;1&quot; || residentNumber.charAt(7) === &quot;2&quot;) ? &quot;19&quot; : &quot;20&quot;;
    var year = yearPrefix + residentNumber.substring(0, 2);
    var month = residentNumber.substring(2, 4);
    var day = residentNumber.substring(4, 6);

    //생년월일 자동으로 채우기
    document.getElementById(&quot;year&quot;).value = year;
    document.getElementById(&quot;month&quot;).value = parseInt(month, 10);
    document.getElementById(&quot;day&quot;).value = parseInt(day, 10);

    return true;
}

//주소 입력
function findAddress() {
    new daum.Postcode({
        oncomplete: function(data) {
            var addr = &#39;&#39;; 
            var extraAddr = &#39;&#39;; 

            if (data.userSelectedType === &#39;R&#39;) {
                addr = data.roadAddress;
            } else { 
                addr = data.jibunAddress;
            }

            document.getElementById(&#39;address&#39;).value = addr;
            document.getElementById(&#39;detailAddress&#39;).value = extraAddr;

        }
    }).open();
}

//주소랑 상세주소 입력 유효성검사
function checkAddress() {
    var address = document.getElementById(&quot;address&quot;).value.trim();
    var address2 = document.getElementById(&quot;detailAddress&quot;).value.trim();

    const addRegex = /^.{1,}$/;

    if(!addRegex.test(address) || !addRegex.test(address2)) {
        alert(&quot;주소와 상세주소를 입력하세요.&quot;);
        return false;
    }
    return true;
}

//관심분야 유효성검사
function checkInterest() {
    var interests = document.querySelectorAll(&quot;#interest input[type=&#39;checkbox&#39;]&quot;);
    let checked = false;
    interests.forEach(cb =&gt; {
        if (cb.checked) checked = true;
    });

    if (!checked) {
        alert(&quot;관심분야를 한 가지 이상 선택해야 합니다.&quot;);
        return false;
    }
    return true;
}

//자기소개 유효성검사
function checkIntroduce() {
    var introduce = document.getElementById(&quot;introduce&quot;).value.trim();
    var introRegex = /^.{20,}$/;

    if(!introRegex.test(introduce)) {
        alert(&quot;자기소개는 20글자 이상은 입력해주세요.&quot;);
        return false;
    }
    return true;
}

//회원가입 버튼 이벤트 (유효성 검사)
document.addEventListener(&quot;DOMContentLoaded&quot;, () =&gt; {
    const form = document.getElementById(&quot;joinForm&quot;);

    form.addEventListener(&quot;submit&quot;, (e) =&gt; {
        if (!checkId() || !checkPassword() || !checkPassword2() || !checkEmail()
         || !checkResidentNum() || !checkAddress() ||!checkInterest() ||!checkIntroduce() ) {
            e.preventDefault(); 
        }
    });
});</code></pre><p><strong>main.jsp</strong></p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&lt;%
    String userId = (String)session.getAttribute(&quot;userId&quot;);
%&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;메인페이지 입니다.&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;%=userId %&gt; 님 로그인 성공 !! &lt;br&gt;

&lt;a href=&quot;logout&quot;&gt;로그아웃&lt;/a&gt; &lt;br&gt;
&lt;a href=&quot;editProfile&quot;&gt;개인정보수정&lt;/a&gt; &lt;br&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] Spring MVC : Model 과 ModelAndView 객체를 사용하여 controller에서 뷰페이지 이동하기]]></title>
            <link>https://velog.io/@heegonkim_/Spring-Spring-MVC-Model-%EA%B3%BC-ModelAndView-%EA%B0%9D%EC%B2%B4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-controller%EC%97%90%EC%84%9C-%EB%B7%B0%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%9D%B4%EB%8F%99%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@heegonkim_/Spring-Spring-MVC-Model-%EA%B3%BC-ModelAndView-%EA%B0%9D%EC%B2%B4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-controller%EC%97%90%EC%84%9C-%EB%B7%B0%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%9D%B4%EB%8F%99%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 12 Nov 2025 11:00:37 GMT</pubDate>
            <description><![CDATA[<h2 id="컨트롤러를-통한-뷰페이지-이동-구현">컨트롤러를 통한 뷰페이지 이동 구현</h2>
<p>사용자의 요청이 발생하면, 컨트롤러는 그 내용에 맞게 데이터값과 이동할 페이지를 알려주어야 한다. 
이때 스프링에서 사용하는 2가지 방식이 있는데 하나는 Model 방식, 또 하나는 ModelAndView 방식이다. 
Model 방식에서는 값만 Model에 저장하여 다음 페이지로 넘겨주는 방식인데 이때 이동할 페이지는 <code>return</code>에 String타입으로 담아서 알려주게 된다. 반면 ModelAndView 방식에서는 ModelAndView의 객체를 생성하여 이 객체에 데이터값과 이동할 페이지 모두를 담아서 <code>return</code> 값으로 ModelAndView의 객체를 넘겨주는 방식으로 이루어진다. </p>
<hr>
<h2 id="controller-컨트롤러">Controller (컨트롤러)</h2>
<p>컨트롤러로 사용할 자바 클래스의 경우 맨 위에 어노테이션 기능을 이용하여 <code>@Controller</code> 라고 명시해준다. 
<img src="https://velog.velcdn.com/images/heegonkim_/post/d1132ce4-1e34-4d05-8198-04a5e866f7d3/image.png" alt="컨트롤러">
1번은 페이지만 이동할 때
2번은 Model 방식으로 값을 가지고 페이지를 이동할 때
3번은 ModelAndView 방식으로 값과 이동할 페이지 정보를 모두 가지고 페이지로 이동할 때
그 각각의 코드이다. </p>
<hr>
<h2 id="페이지만-이동하는-경우">페이지만 이동하는 경우</h2>
<pre><code>@RequestMapping(&quot;/view&quot;)
public String view() {
    return &quot;board/view&quot;;
}</code></pre><p>데이터값을 넘겨줄 필요는 없고, 단순히 페이지 이동만 필요한 경우에는 위와 같이 작성한다. <code>@RequestMapping(&quot;/view&quot;)</code> 부분에는 url 경로를 지정해주면 된다.
웹프로젝트를 서버에서 실행해주고, url 부분의 뒷부분에 &#39;/view&#39;를 추가로 입력해주면 return 부분에 따라서 board 폴더 안에 있는 view.jsp 페이지로 이동하게 된다. </p>
<p><strong>view.jsp</strong></p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot; %&gt;    
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt; viewpage&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
view.page 입니다. &lt;br&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><hr>
<h2 id="model을-이용하여-값을-가지고-페이지를-이동하는-경우">Model을 이용하여 값을 가지고 페이지를 이동하는 경우</h2>
<pre><code>@RequestMapping(&quot;/content&quot;)
public String content(Model model) {
    model.addAttribute(&quot;id&quot;, &quot;heegon&quot;);
    return &quot;board/content&quot;;
}</code></pre><p>Model을 이용하여 값을 가진 상태에서 페이지를 이동하는 경우에는 페이지만 이동하는 경우와 마찬가지로 String 타입의 반환값을 가진 메소드를 만들어준다. 아까와 다른 점은 매개변수 부분에 <code>Model model</code>과 같이 Model의 객체를 넣어준다는 점이다. 그리고 메소드 내부에서 <code>model.addAttribute(&quot;jsp페이지에서 사용하는 데이터의 id명&quot;, 해당 데이터에 넣을 데이터값)</code>과 같은 형태로 작성된다. </p>
<p><strong>content.jsp</strong></p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;content1 page&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
content 페이지 입니다. &lt;br&gt;
id는 ${id } 입니다. &lt;br&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><hr>
<h2 id="modelandview를-이용하여-값과-이동할-페이지를-모두-가진-상태로-페이지를-이동하는-경우">ModelAndView를 이용하여 값과 이동할 페이지를 모두 가진 상태로 페이지를 이동하는 경우</h2>
<pre><code>@RequestMapping(&quot;/content2&quot;)
public ModelAndView content2() {

    ModelAndView mv = new ModelAndView();
    mv.addObject(&quot;name&quot;, &quot;heegonkim&quot;);
    mv.setViewName(&quot;board/content2&quot;);

    return mv;
}</code></pre><p>ModelAndView 방식에서는 위와 같이 문법이 살짝 다르다. Model방식에서는 addAttribute를 사용하여 데이터값을 넣어주었다면 ModelAndView 방식에서는 <code>addObject()</code>를 통해서 데이터값을 넣어주고, <code>setViewName()</code>을 통해서 이동할 뷰페이지를 알려준다. 그리고 마지막 <code>return</code> 부분에는 이전에 String타입으로 이동할 뷰페이지를 지정했던 것과 다르게 ModelAndView의 객체를 반환해주는 방식으로 끝나게 된다. </p>
<p><strong>content2.jsp</strong></p>
<pre><code>&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot; %&gt; 
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;EUC-KR&quot;&gt;
&lt;title&gt;content2 page&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
content2 페이지 입니다. &lt;br&gt;
이름은 ${name } 입니다. &lt;br&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><hr>
<h2 id="정리">정리</h2>
<p><strong>정리하자면 다음과 같다.</strong></p>
<ul>
<li><p>단순히 페이지만 이동하면 되는 경우에는</p>
<pre><code>public String 메소드명() { 
  return &quot;뷰페이지 경로&quot;; 
}</code></pre></li>
<li><p>값을 가지고 Model을 이용해서 페이지를 이동하는 경우에는</p>
<pre><code>public String 메소드명(Model model) {
  model.addAttribute(&quot;데이터의 id값&quot;, 데이터값);
  return &quot;뷰페이지 경로&quot;;
}</code></pre></li>
<li><p>값과 페이지를 한 번에 저장하여 페이지를 이동하는 경우에는</p>
<pre><code>public ModelAndView 메소드명() {
  ModelAndView 모델앤뷰객체 = new ModelAndView();
  모델앤뷰객체.addObject(&quot;데이터의 id값&quot;, 데이터값);
  모델앤뷰객체.setViewName(&quot;뷰페이지 경로&quot;);
  return 모델앤뷰객체;
}</code></pre></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 스프링 AOP 이해를 위한 초간단 코드 만들어보기]]></title>
            <link>https://velog.io/@heegonkim_/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-AOP-%EC%9D%B4%ED%95%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%B4%88%EA%B0%84%EB%8B%A8-%EC%BD%94%EB%93%9C-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@heegonkim_/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-AOP-%EC%9D%B4%ED%95%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%B4%88%EA%B0%84%EB%8B%A8-%EC%BD%94%EB%93%9C-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Fri, 07 Nov 2025 07:19:13 GMT</pubDate>
            <description><![CDATA[<p><strong>Human.java</strong></p>
<pre><code>public class Human {

    private String name;
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    public void getHumanInfo() {
        System.out.println(&quot;이름 : &quot; + getName());
        System.out.println(&quot;나이 : &quot; + getAge());
    }

}</code></pre><p><strong>MainClass.java</strong></p>
<pre><code>public class MainClass {

    public static void main(String[] args) {

        AbstractApplicationContext ctx
        = new GenericXmlApplicationContext(&quot;classpath:applicationCTX.xml&quot;);

        Human human1 = ctx.getBean(&quot;human&quot;, Human.class);
        human1.getHumanInfo();

        ctx.close();
    }

}</code></pre><p><strong>applicationCTX.xml</strong></p>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xmlns:aop=&quot;http://www.springframework.org/schema/aop&quot;
    xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd&quot;&gt;

&lt;bean id=&quot;logAop&quot; class=&quot;kr.study.spring.LogAop&quot; /&gt;

&lt;aop:config&gt;
    &lt;aop:aspect id=&quot;logger&quot; ref=&quot;logAop&quot;&gt;
        &lt;aop:pointcut id=&quot;pointcut1&quot; expression=&quot;within(kr.study.spring..*)&quot; /&gt;
        &lt;aop:around pointcut-ref=&quot;pointcut1&quot; method=&quot;aopMethod&quot; /&gt;
    &lt;/aop:aspect&gt;
&lt;/aop:config&gt;


&lt;bean id=&quot;human&quot; class=&quot;kr.study.spring.Human&quot;&gt;
    &lt;property name=&quot;name&quot; value=&quot;희곤&quot;&gt;&lt;/property&gt;
    &lt;property name=&quot;age&quot; value=&quot;20&quot;&gt;&lt;/property&gt;
&lt;/bean&gt;

&lt;/beans&gt; 
</code></pre><p><strong>LogAop.java</strong></p>
<pre><code>public class LogAop {

    public Object aopMethod(ProceedingJoinPoint jp) throws Throwable {

        System.out.println(&quot;실행 전&quot;);
        try {
            Object obj = jp.proceed();
            return obj;
        } finally {
            System.out.println(&quot;실행 끝&quot;);
        }
    }
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 스프링 AOP 공부하기]]></title>
            <link>https://velog.io/@heegonkim_/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-AOP-%EA%B3%B5%EB%B6%80%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@heegonkim_/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-AOP-%EA%B3%B5%EB%B6%80%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 06 Nov 2025 12:18:27 GMT</pubDate>
            <description><![CDATA[<h2 id="개념">개념</h2>
<p><em>&#39;핵심 기능 코드&#39;</em> 와 <em>&#39;공통 기능 코드&#39;</em> 를 <strong>분류시켜</strong> 만드는 방식.
<strong>(같은 기능이 여러 번 사용되는 공통 기능 코드의 경우, 중복 반복 작성되기 때문이다.)</strong></p>
<p>여기서 공통 기능이란 자주 사용하는 코드들을 따로 빼서 모아놓는 것을 말한다. 
그리고 핵심 기능을 실행할 때, 특정 시기(실행 전/실행 후/실행 전후 등)에 공통 기능을 실행해주는 방식으로 사용된다.</p>
<p>ex) 예를 들면, 
<code>System.out.println(&quot;메소드 실행 시작!&quot;)</code> -&gt; 메소드 실행 시작 전마다 이 기능을 작동시킬 것이므로 <strong>공통 기능</strong>으로 분류하여 구현. 
<code>public void doPlay1()</code> -&gt; 몇 번 실행하지 않으므로 <strong>핵심 기능</strong>으로 분류.</p>
<hr>
<h2 id="기본-용어">기본 용어</h2>
<ul>
<li><strong>Aspect</strong> : <em>공통 기능</em> 을 의미한다.</li>
<li><strong>Advice</strong> : <em>공통 기능의 기능 자체</em> 를 의미한다.</li>
<li><strong>JoinPoint</strong> : 공통 기능을 <em>적용해야 하는 부분</em> (스프링에서 메소드)</li>
<li><strong>Pointcut</strong> : 공통 기능의 일부분으로 <em>실제로 공통 기능이 적용</em> 되는 부분</li>
<li><strong>Weaving</strong> : 공통 기능을 핵심 기능에 <em>적용하는 행위</em> 를 의미.</li>
</ul>
<hr>
<h2 id="aop-적용-방식-1--xml">AOP 적용 방식 1 : XML</h2>
<p><strong>✅pom.xml</strong></p>
<p>가장 먼저 pom.xml 파일에서 의존 설정을 해줘야 한다. 
아래 2개를 추가해주면 된다. version은 잘 맞춰주어야 한다. </p>
<pre><code>&lt;!-- cglib --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;cglib&lt;/groupId&gt;
    &lt;artifactId&gt;cglib&lt;/artifactId&gt;
    &lt;version&gt;3.2.12&lt;/version&gt;
&lt;/dependency&gt;

&lt;!-- AOP --&gt;
&lt;!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.aspectj&lt;/groupId&gt;
    &lt;artifactId&gt;aspectjweaver&lt;/artifactId&gt;
    &lt;version&gt;1.7.4&lt;/version&gt;
&lt;/dependency&gt;</code></pre><p><strong>✅Student.java</strong></p>
<p>데이터 클래스 1번</p>
<pre><code>public class Student {

    private String name;
    private int age;
    private int gradeNum;
    private int classNum;

    public void getStudentInfo() {
        System.out.println(&quot;이름 : &quot; + getName());
        System.out.println(&quot;나이 : &quot; + getAge());
        System.out.println(&quot;학년 : &quot; + getGradeNum());
        System.out.println(&quot;반 번호 : &quot; + getClassNum());
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getGradeNum() {
        return gradeNum;
    }
    public void setGradeNum(int gradeNum) {
        this.gradeNum = gradeNum;
    }
    public int getClassNum() {
        return classNum;
    }
    public void setClassNum(int classNum) {
        this.classNum = classNum;
    }

}</code></pre><p><strong>✅Worker.java</strong></p>
<p>데이터 클래스 2번</p>
<pre><code>public class Worker {

    private String name;
    private int age;
    private String job;

    public void getWorkerInfo() {
        System.out.println(&quot;이름 : &quot; + getName());
        System.out.println(&quot;나이 : &quot; + getAge());
        System.out.println(&quot;직업 : &quot; + getJob());
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

}</code></pre><p><strong>✅MainClass.java</strong></p>
<p>앞 글인 DI 의존주입에서 사용한 Main 클래스와 다른 점이 없다. 
이번에는 xml 방식으로 의존 주입을 하였다. </p>
<pre><code>public class MainClass {

    public static void main(String[] args) {

        AbstractApplicationContext ctx
        = new GenericXmlApplicationContext(&quot;classpath:applicationCTX.xml&quot;);

        Student student = ctx.getBean(&quot;student&quot;, Student.class);
        student.getStudentInfo();

        System.out.println(&quot;=============&quot;);

        Worker worker = ctx.getBean(&quot;worker&quot;, Worker.class);
        worker.getWorkerInfo();

        ctx.close();

    }

}</code></pre><p><img src="https://velog.velcdn.com/images/heegonkim_/post/226e9695-0956-4768-9e15-2aeebdf3315d/image.png" alt="어노테이션"></p>
<p><strong>✅applicationCTX.xml</strong></p>
<p><strong>기본 형태 설명</strong></p>
<pre><code>&lt;bean id=&quot;아이디&quot; class=&quot;패키지.메소드&quot; /&gt;

&lt;aop:config&gt;
    &lt;aop:aspect id=&quot;식별자&quot; ref=&quot;아이디&quot;&gt;
        &lt;aop:pointcut id=&quot;포인트컷이름&quot; expression=&quot;적용범위 표현식&quot; /&gt;
        &lt;aop:적용스타일 pointcut-ref=&quot;포인트컷이름&quot; method=&quot;Advice메서드 이름&quot; /&gt;
    &lt;/aop:aspect&gt;
&lt;/aop:config&gt;</code></pre><p><strong>적용 스타일</strong>
<code>aop:around</code> -&gt; 대상 메소드 실행 전후 모두 제어 가능 (인자 반드시 필요)
<code>aop:before</code> -&gt; 실행 전
<code>aop:after-returning</code> -&gt; 정상 종료 후
<code>aop:after-throwing</code> -&gt; 예외 발생 시
<code>aop:after</code> -&gt; 항상 마지막에 실행 (예외처리 구문 중 finally 와 유사)</p>
<p><strong>적용범위 표현식</strong>
&#39;execution, within, bean&#39; 이렇게 3가지가 있다. 
<code>expression=&quot;within(kr.study.spring.*)&quot;</code> -&gt; kr.study.spring 패키지 바로 아래에 있는 클래스들 안의 메소드에 적용.
<code>expression=&quot;within(kr.study.spring..*)</code> -&gt; kr.study.spring 패키지의 하위 패키지 전부의 클래스 안의 메소드까지 적용.
<code>expression=&quot;execution(* kr.study.spring.Worker.*())&quot;</code> -&gt; Worker 클래스의 메소드에만 적용.
<code>expression=&quot;bean(student)&quot;)</code> -&gt; student 라는 이름을 가진 클래스의 메소드에만 적용
<code>expression=&quot;bean(*ker)&quot;)</code> -&gt; ~ker로 끝나는 클래스의 메소드에만 적용된다.</p>
<p>⭐⭐⭐ 그러니까 쉽게 생각하자면, 현재 Student 클래스가 있다. 이 클래스에게만 AOP를 적용하고 싶다면 <code>kr.study.spring.student</code> 이렇게 적용범위 표현식을 사용하면 된다. 어떤 클래스의 앞뒤, 혹은 앞, 혹은 뒤에 공통 기능을 넣을지를 적용범위 표현식에서 지정한다고 생각하면 된다. 앞뒤, 앞, 뒤 같은 타이밍에 관한 것은 위의 적용 스타일로 지정한다. </p>
<p>예시 코드</p>
<pre><code>&lt;!-- AOP 관련 --&gt;
    &lt;bean id=&quot;logAop&quot;  class=&quot;kr.study.spring.LogAop&quot; /&gt;

    &lt;aop:config&gt;
        &lt;aop:aspect id=&quot;logger&quot; ref=&quot;logAop&quot;&gt;
            &lt;aop:pointcut id=&quot;publicM&quot; expression=&quot;within(kr.study.spring.*)&quot; /&gt;
            &lt;aop:around pointcut-ref=&quot;publicM&quot; method=&quot;loggerAop&quot; /&gt;
        &lt;/aop:aspect&gt;

        &lt;aop:aspect id=&quot;logger&quot; ref=&quot;logAop&quot;&gt;
            &lt;aop:pointcut id=&quot;publicM&quot; expression=&quot;within(kr.study.spring.*)&quot; /&gt;
            &lt;aop:before pointcut-ref=&quot;publicM&quot; method=&quot;beforeAdvice&quot; /&gt;
        &lt;/aop:aspect&gt;

        &lt;aop:aspect id=&quot;logger&quot; ref=&quot;logAop&quot;&gt;
            &lt;aop:pointcut id=&quot;publicM&quot; expression=&quot;within(kr.study.spring.*)&quot; /&gt;
            &lt;aop:after-returning pointcut-ref=&quot;publicM&quot; method=&quot;afterReturningAdvice&quot; /&gt;
        &lt;/aop:aspect&gt;

        &lt;aop:aspect id=&quot;logger&quot; ref=&quot;logAop&quot;&gt;
            &lt;aop:pointcut id=&quot;publicM&quot; expression=&quot;within(kr.study.spring.*)&quot; /&gt;
            &lt;aop:after-throwing pointcut-ref=&quot;publicM&quot; method=&quot;afterAdvice&quot; /&gt;
        &lt;/aop:aspect&gt;
    &lt;/aop:config&gt;</code></pre><p><strong>✅LogAop.java</strong></p>
<p>⭐ 공통 기능을 담은 클래스 (AOP 클래스) ⭐
=&gt; 공통으로 적용할 로직을 모아두는 클래스이다. 
=&gt; 여러 번 사용하는 기능(클래스나 메소드)를 모아두었다고 생각하면 쉽다. </p>
<p>자주 쓸 것 같은 코드 구조 1 (메소드의 실행 전후 모두 적용 가능)</p>
<pre><code>public Object 메소드명(ProceedingJoinPoint pjp) throws Throwable {
    //전 처리
    Object result = pjp.proceed(); //실제 메소드 실행
    //후 처리
    return result;
}</code></pre><p>여기에서 <code>pjp.getSignature()</code> 는 현재 실행 중인 메소드의 정보를 가져오는 메소드이다.</p>
<p>예시 코드</p>
<pre><code>public class LogAop {

    //공통 기능 메소드 1 (메소드 실행 전후 제어)
    public Object loggerAop(ProceedingJoinPoint joinpoint) throws Throwable {
        String signatureStr = joinpoint.getSignature().toShortString();
        System.out.println(signatureStr + &quot; is start.&quot;);
        long startTime = System.currentTimeMillis();
        //System.currentTimeMillis() -&gt; 현재 시간을 ms초로 알려줌.

        try {
            Object obj = joinpoint.proceed();
            return obj;
        } finally {
            long endTime = System.currentTimeMillis();
            System.out.println(signatureStr + &quot; is finished.&quot;);
            System.out.println(signatureStr + &quot; 경과시간 : &quot; + (endTime - startTime));
        }
    }

    //실행 전
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println(&quot;beforeAdvice()&quot;);
    }

    //정상 종료 후
    public void afterReturningAdvice() {
        System.out.println(&quot;afterReturningAdvice()&quot;);
    }

    //예외 발생 후
    public void afterThrowingAdvice() {
        System.out.println(&quot;afterThrowingAdvice()&quot;);
    }

    //정상 종료 혹은 예외 발생 그 최후 마지막에 실행
    public void afterAdvice() {
        System.out.println(&quot;afterAdvice()&quot;);
    }
}</code></pre><hr>
<h2 id="aop-적용-방식-2--어노테이션">AOP 적용 방식 2 : 어노테이션</h2>
<p>pom.xml, MainClass.java, Student.java, Worker.java 의 코드는 위와 동일하다. </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/7dc56096-bda0-47ae-b377-6c2570d382a5/image.png" alt="어노테이션"></p>
<p><strong>✅applicationCTX.xml</strong></p>
<pre><code>&lt;!-- AOP 관련 --&gt;
&lt;aop:aspectj-autoproxy /&gt;
&lt;bean id=&quot;logAop&quot; class=&quot;kr.study.spring.LogAop&quot; /&gt;</code></pre><p>놀랍게도 이게 다다. </p>
<p><strong>✅LogAop.java</strong></p>
<p><code>@Aspect</code> -&gt; AOP 클래스 임을 표현(?)</p>
<p><code>@Pointcut(&quot;범위 지정 표현식&quot;)</code> -&gt; PointCut</p>
<p><code>@Around(&quot;포인트컷 메소드()&quot;)</code> -&gt; around, before, after 등등 명시</p>
<p>예시 코드</p>
<pre><code>@Aspect
public class LogAop {

    @Pointcut(&quot;within(kr.study.spring.*)&quot;)
    private void pointcutMethod() {
    }

    @Around(&quot;pointcutMethod()&quot;)
    public Object loggerAop(ProceedingJoinPoint joinpoint) throws Throwable {
        String signatureStr = joinpoint.getSignature().toShortString();
        System.out.println(signatureStr + &quot; is start.&quot;);
        long st = System.currentTimeMillis();

        try {
            Object obj = joinpoint.proceed();
            return obj;
        } finally {
            long et = System.currentTimeMillis();
            System.out.println(signatureStr + &quot; is finished.&quot;);
            System.out.println(signatureStr + &quot; 경과시간 : &quot; + (et - st));
        }
    }

    @Before(&quot;pointcutMethod()&quot;)
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println(&quot;beforeAdvice()&quot;);
    }

    @AfterReturning(&quot;pointcutMethod()&quot;)
    public void afterReturningAdvice() {
        System.out.println(&quot;afterReturningAdvice()&quot;);
    }


    @AfterThrowing(&quot;pointcutMethod()&quot;)
    public void afterThrowingAdvice() {
        System.out.println(&quot;afterThrowingAdvice()&quot;);
    }


    @After(&quot;pointcutMethod()&quot;)
    public void afterAdvice() {
        System.out.println(&quot;afterAdvice()&quot;);
    }
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] Spring 시작하기]]></title>
            <link>https://velog.io/@heegonkim_/Spring-Spring</link>
            <guid>https://velog.io/@heegonkim_/Spring-Spring</guid>
            <pubDate>Mon, 03 Nov 2025 04:49:52 GMT</pubDate>
            <description><![CDATA[<h2 id="spring-설치-하는-법-스프링-stsexe">Spring 설치 하는 법 (스프링, STS.exe)</h2>
<p>✔️ 스프링 프레임워크의 설치는 javaJDK 설치가 완벽하게 끝나고, Path 경로까지 지정한 후에 진행해야 한다. </p>
<p>JDK 는 11 버전을, Path 경로는 아래와 같이 진행하였다. 
<a href="https://velog.io/@heegonkim_/Java-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95">jdk 설치하고, path 경로 지정하는 방법</a></p>
<p>한 마디로 이클립스 위에서 자바 클래스가 온전하게 실행되는 상태에서 진행한다고 생각하면 편하다.</p>
<p>스프링의 경우 Spring 3.0.0 버전과 Spring 4.0.0 버전이 있는데, 스프링 프레임워크를 사용하기 위해서는 3점대 버전을 사용해야 한다. 4점대 버전부터는 스프링 &#39;부트&#39;이기 때문이다. </p>
<p>먼저, 스프링 3점대 버전을 구해서 설치해주어야 한다. 설치는 어렵지 않다. </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/6732e553-5541-498f-8773-929072b270c4/image.png" alt="프로젝트생성"></p>
<p>문제는 설치를 마친 이후에, 처음 STS.exe 를 실행해주었을 때 프로젝트 생성을 위해 Spring Legacy Project를 선택해주는데 </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/18e26d31-e8c9-434d-9acb-652f6ae3ebc6/image.png" alt="프로젝트생성2"></p>
<p>위 이미지와 달리, Spring MVC Project 라는 선택지가 없다. </p>
<p>✔️ Spring MVC Project 프로젝트를 생성하기 위해서는 아래와 같이 진행하면 된다. </p>
<p><code>작업 폴더 위치</code> 아래에 <code>.metadata</code> 이 아래에  <code>.plugins</code>이 아래에 있는 <code>org.springsource.ide.eclipse.commons.content.core</code> 폴더를 찾아야 한다. </p>
<p>이 안에 <code>https-content.xml</code> 파일을 넣어주어야 한다. 
https-context.xml 파일은 웹서핑을 통해서 어렵지 않게 구할 수 있다. </p>
<p>✔️ 여기까지 완료가 되면, STS.exe를 완전히 종료한 이후에 다시 실행하여 프로젝트를 생성하면 위 이미지와 같이 &#39;Spring MVC Project&#39; 선택지가 생긴 것을 확인할 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/74f4e5e1-24dd-4db7-ad53-dfa7833f1dc8/image.png" alt=""></p>
<p>next 버튼을 누르게 되면 다음과 같은 화면이 나오는데, 도메인을 지정해주는 설정 사항이다. 반드시 <code>단어1.단어2.단어3</code> 와 같이, 3개의 구조로 이뤄주어야 한다. </p>
<p>예를 들면, <code>com.mycompany.myapp</code>, <code>kr.blog.velog</code> 와 같다. </p>
<p>✔️ 여기까지 진행했으면 이 이후에 Tomcat 서버를 스프링 위에 얹어주면 된다. </p>
<h2 id="spring-legacy-project---spring-mvc-project-폴더-구조">Spring Legacy Project -&gt; Spring MVC Project 폴더 구조</h2>
<p><img src="https://velog.velcdn.com/images/heegonkim_/post/8820064c-c0aa-4aae-99df-ecef12858ccc/image.png" alt="스프링MVC프로젝트 폴더구조"></p>
<p>자바 파일은 src/main/java 폴더 아래에, 
xml 파일은 src/main/resources 폴더 아래에, 
jsp 파일은 src/main/webapp/views 폴더 아래에 만들어주면 된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 스프링에서 DI 의존성 주입 방법]]></title>
            <link>https://velog.io/@heegonkim_/Spring-%EC%8A%A4%ED%94%84%EB%A7%81%EC%97%90%EC%84%9C-DI-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@heegonkim_/Spring-%EC%8A%A4%ED%94%84%EB%A7%81%EC%97%90%EC%84%9C-DI-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 03 Nov 2025 04:08:31 GMT</pubDate>
            <description><![CDATA[<p>다른 클래스의 메소드나 필드를 사용할 때, 그 클래스의 객체를 생성하여 내부 필드와 메소드에 접근할 수 있다. </p>
<p>문제는 여러 곳(여러 외부)에서 해당 클래스의 필드와 메소드가 필요하다면 매번 객체를 생성해주어야 한다는 것이다.
스프링 웹 프레임워크에서는 이를 해결해주기 위해 객체를 생성할 수 있게 미리 세팅을 해놓고 필요할 때만 불러와서 쓸 수 있게 해놓았다. 이를 <strong>의존성 주입 (DI)</strong> 라고 한다. 그 방법에는 크게 2가지가 있다. </p>
<ul>
<li>즉, 자바 빈즈 (데이터 클래스)를 만들고 이를 외부에서 사용하는 방법은 크게 3가지이다.<ol>
<li>객체 생성하여 사용하기 (기존 방식) </li>
<li>XML 파일 이용하기</li>
<li>어노테이션 사용하여 자바 파일 이용하기</li>
</ol>
</li>
</ul>
<hr>
<h2 id="✅-방법-1번-객체-만들어-사용하기">✅ 방법 1번. 객체 만들어 사용하기</h2>
<p>ex)</p>
<p><strong>자바빈즈 클래스 (데이터 클래스)</strong></p>
<pre><code>public class Human {

    //필드 2개 (name, age)
    private String name;
    private int age;

    //필드 2개에 따른 setter, getter 메소드
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}</code></pre><p><strong>메인 클래스 (외부 클래스)</strong></p>
<pre><code>public class MainClass {
    public static void main(String[] args) {
        Human human = new Human();
        human.setName(&quot;사람1&quot;);
        human.setAge(20);
        System.out.println(human.getName() + &quot; 의 나이는 &quot; + human.getAge() + &quot; 입니다.&quot;);
    }
}</code></pre><p><strong>실행 결과</strong></p>
<blockquote>
<p>사람1 의 나이는 20 입니다.</p>
</blockquote>
<hr>
<h2 id="✅-방법-2번-xml-파일을-이용하는-방법">✅ 방법 2번. XML 파일을 이용하는 방법.</h2>
<p>xml 파일 생성 방법 : file -&gt; new -&gt; Spring Bean Configuration File
(src/main/resources 폴더에 넣어주면 된다.)</p>
<p>⭐⭐⭐ <strong>핵심</strong> ⭐⭐⭐
<code>AbstractApplicationContext ctx = new GenericXmlApplicationContext(&quot;classpath:applicationCTX.xml&quot;);</code></p>
<h3 id="✅✅-xml방식1-setter-방식">✅✅ XML방식1 (setter 방식)</h3>
<p>xml에서 <property> 태그를 이용해서 필드에 값 넣어준다. </p>
<p><strong>자바빈즈 클래스 (데이터 클래스)</strong></p>
<pre><code>public class Human {

    //필드 2개 (name, age)
    private String name;
    private int age;

    //필드 2개에 따른 setter, getter 메소드
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}</code></pre><p><strong>메인 클래스 (외부 클래스)</strong>
setter로 사용할 사용 방법
<code>AbstractApplicationContext 객체명 = new GenericXmlApplicationContext(&quot;classpath:xml파일명.xml&quot;)</code></p>
<pre><code>public class MainClass {
    public static void main(String[] args) {
          //⭐핵심
        AbstractApplicationContext ctx
            = new GenericXmlApplicationContext(&quot;classpath:applicationCTX.xml&quot;);

        //객체 사용이라면 Human human = new Human();
          //=new Human() 대신, =ctx.getBean(&quot;구분용 아이디&quot;, 빈즈클래스명.class) 사용
        Human human = ctx.getBean(&quot;humanDataClass&quot;, Human.class);

        //setter 역할을 xml 파일이 대신해준다. 
        //바로 getter 메소드로 값만 받아와서 외부에서 값 사용하면 된다. 
        System.out.println(&quot;이름은 &quot; + human.getName() + &quot; 이고, 나이는 &quot; + human.getAge() + &quot; 입니다.&quot;);

          //객체 사용 종료
        ctx.close();
    }
}</code></pre><p><strong>applicationCTX.xml</strong></p>
<p>setter 역할을 해주는 xml 파일</p>
<p>java 파일 위치 -&gt; src/main/java
xml 파일 위치 -&gt; src/main/resources
xml파일 위치에서 file -&gt; new -&gt; Spring Bean Configuration File 로 xml 파일 생성하면 된다. </p>
<pre><code>&lt;bean id=&quot;humanDataClass&quot; class=&quot;heegon.study.spring.Human&quot;&gt;
    &lt;property name=&quot;name&quot;&gt;
        &lt;value&gt;희곤&lt;/value&gt;
    &lt;/property&gt;
    &lt;property name=&quot;age&quot;&gt;
        &lt;value&gt;20&lt;/value&gt;
    &lt;/property&gt;
&lt;/bean&gt;</code></pre><p><strong>실행 결과</strong></p>
<blockquote>
<p>이름은 희곤 이고, 나이는 20 입니다.</p>
</blockquote>
<h3 id="✅✅-xml방식2-생성자-방식">✅✅ XML방식2 (생성자 방식)</h3>
<p>xml에서 <constructor-arg> 태그를 이용해서 필드에 값 넣어준다. </p>
<p><strong>데이터 클래스</strong></p>
<pre><code>public class Human {

        //필드 2개 (name, age)
        private String name;
        private int age;

        public Human(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }


        public int getAge() {
            return age;
        }


        public void printHuman() {
            System.out.println(&quot;이름은 &quot; + getName() + &quot; 이고, 나이는 &quot; + getAge() + &quot; 입니다.&quot;);
        }

}</code></pre><p><strong>메인 클래스(외부 클래스)</strong></p>
<pre><code>public class MainClass {
    public static void main(String[] args) {
          //⭐핵심
        AbstractApplicationContext ctx
            = new GenericXmlApplicationContext(&quot;classpath:applicationCTX.xml&quot;);

        //객체 사용이라면 Human human = new Human();
        Human human = ctx.getBean(&quot;humanDataClass&quot;, Human.class);

        //setter 역할을 xml 파일이 대신해준다. 
        //getter 메소드가 없기 때문에, 출력코드를 만들어준 Human 클래스의 printHuman() 메소드를 불러와 사용한다. 
        human.printHuman();

          //객체 사용 종료
        ctx.close();
    }
}  </code></pre><p><strong>applicationCTX.xml</strong>
setter메소드를 이용할 때는 <code>&lt;property&gt;</code> 태그를 이용하고, 
생성자 함수로 필드에 값을 넣을 때는 <code>&lt;constructor-arg&gt;</code> 태그를 이용한다. </p>
<pre><code>    &lt;bean id=&quot;humanDataClass&quot; class=&quot;heegon.study.spring.Human&quot;&gt;
        &lt;constructor-arg&gt;
            &lt;value&gt;희곤&lt;/value&gt;
        &lt;/constructor-arg&gt;

        &lt;constructor-arg&gt;
            &lt;value&gt;20&lt;/value&gt;
        &lt;/constructor-arg&gt;
    &lt;/bean&gt;</code></pre><p><strong>실행 결과</strong></p>
<blockquote>
<p>이름은 희곤 이고, 나이는 20 입니다.</p>
</blockquote>
<h3 id="✅✅-xml방식3-arraylist와-같이-여러-값-넣기">✅✅ XML방식3 (ArrayList와 같이 여러 값 넣기)</h3>
<p><strong>MainClass.java</strong></p>
<pre><code>public class MainClass {

    public static void main(String[] args) {

        AbstractApplicationContext ctx 
              = new GenericXmlApplicationContext(&quot;classpath:applicationCTX.xml&quot;);

        Human human = ctx.getBean(&quot;humanDataClass&quot;, Human.class);

        System.out.println(&quot;===========&quot;);
        System.out.print(&quot;이름 : &quot;);
        System.out.println(human.getName());
        System.out.print(&quot;취미는 : &quot;);
        System.out.println(human.getHobbys());
        System.out.print(&quot;나이는 : &quot;);
        System.out.println(human.getAge());
        System.out.println(&quot;===========&quot;);

        ctx.close();
    }

}
</code></pre><p><strong>Human.java</strong></p>
<p>ArrayList<String> 타입의 hobbys 변수가 존재한다. 즉, 여러개의 값을 넣어야 하는 경우가 생겼다.   </p>
<pre><code>public class Human {

    private String name;
    private int age;
    private ArrayList&lt;String&gt; hobbys;

    Human(String name, ArrayList&lt;String&gt; hobbys){
        this.name = name;
        this.hobbys = hobbys;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public ArrayList&lt;String&gt; getHobbys() {
        return hobbys;
    }

    public void setHobbys(ArrayList&lt;String&gt; hobbys) {
        this.hobbys = hobbys;
    }

}
</code></pre><p><strong>applicationCTX.xml</strong></p>
<p>  아래와 같이 <list></list> 태그 내부에 <value>값</value> 을 넣어주기만 하면 된다. </p>
<pre><code>&lt;bean id=&quot;humanDataClass&quot; class=&quot;kr.study.spring.Human&quot;&gt;
        &lt;constructor-arg&gt;
            &lt;value&gt;희곤&lt;/value&gt;
        &lt;/constructor-arg&gt;

        &lt;constructor-arg&gt;
            &lt;list&gt;
                &lt;value&gt;넷플릭스&lt;/value&gt;
                &lt;value&gt;운동&lt;/value&gt;
                &lt;value&gt;배틀그라운드&lt;/value&gt;
            &lt;/list&gt;
        &lt;/constructor-arg&gt;

        &lt;property name=&quot;age&quot; value=&quot;20&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt; </code></pre><hr>
<h2 id="✅-방법-3번-어노테이션을-사용하는-방법">✅ 방법 3번. 어노테이션을 사용하는 방법.</h2>
<p>(xml 파일 없이 자바 클래스 파일만으로 가능)</p>
<p>⭐⭐⭐ <strong>핵심</strong> ⭐⭐⭐
<code>AnnotationConfigApplicationContext 객체명 = new AnnotationConfigApplicationContext(파일명.class)</code></p>
<h3 id="✅✅-어노테이션-방식1-setter-getter-메소드만으로-구성된-데이터-클래스">✅✅ 어노테이션 방식1 (setter, getter 메소드만으로 구성된 데이터 클래스)</h3>
<p><strong>Human.java</strong></p>
<p>setter와 getter로만 이루어진 (생성자함수 사용 X) 데이터 클래스  </p>
<pre><code>public class Human {

    private String name;
    private int age;

      public String getName() {
            return name;
    }

    public void setName(String name) {
            this.name = name;
    }

    public int getAge() {
            return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}</code></pre><p><strong>MainClass.java</strong></p>
<p>xml방식과 마찬가지로, ctx 객체를 만들어서 .getBean() 메소드를 통해서 데이터값들을 가져올 수 있다. 
xml방식과 달리 AnnotationConfigApplicaionContext 클래스를 이용해 ctx 객체를 생성한다. 
= new AnnotationConfigApplicationContext(xml역할을 대신해주는 java코드파일명.class); 이다. </p>
<pre><code>public class MainClass {
    public static void main(String[] args) {

        AnnotationConfigApplicationContext ctx
        = new AnnotationConfigApplicationContext(ApplicationConfig.class);

        Human human1 = ctx.getBean(&quot;human1&quot;, Human.class);
        System.out.println(&quot;이름 : &quot; + human1.getName());
        System.out.println(&quot;나이 : &quot; + human1.getAge());

        ctx.close();
    }
}</code></pre><p><strong>ApplicationConfig.java</strong></p>
<p>xml의 역할을 해주는 자바 클래스를 만들고 @Configuration 어노테이션을 사용해서 알려준다. xml 방식과 다른 점은, 데이터 클래스인 Human 의 객체를 만들어준다음에, 내부 메소드(지금은 setter)를 사용해 값을 넣어주고, 마지막에 <code>return 클래스 객체;</code> 를 통해서 다시 객체를 반환해주어야 한다는 것이다. </p>
<pre><code>@Configuration
public class ApplicationConfig {

    @Bean
    public Human human1() {
        Human human = new Human();
        human.setName(&quot;희곤&quot;);
        human.setAge(20);

        return human;
    }
}</code></pre><p><strong>pom.xml</strong></p>
<p>추가적으로 자바 클래스를 이용한 어노테이션 방식에서는 반드시 pom 파일 안에 아래와 같이 <dependency> 내용을 추가로 넣어주어야 동작된다.  </p>
<pre><code>&lt;dependency&gt;
    &lt;groupId&gt;cglib&lt;/groupId&gt;
    &lt;artifactId&gt;cglib&lt;/artifactId&gt;
    &lt;version&gt;2.2.2&lt;/version&gt;
&lt;/dependency&gt;</code></pre><h3 id="✅✅-어노테이션-방식2-생성자-함수를-이용한-데이터-클래스">✅✅ 어노테이션 방식2 (생성자 함수를 이용한 데이터 클래스)</h3>
<p><strong>Human.java</strong></p>
<pre><code>  public class Human {

        //필드 2개 (name, age)
        private String name;
        private int age;

          //생성자 함수로 이름과 나이를 넣어줌.
        public Human(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }

}</code></pre><p>  <strong>MainClass.java</strong></p>
<pre><code>  public class MainClass {
    public static void main(String[] args) {

        AnnotationConfigApplicationContext ctx
        = new AnnotationConfigApplicationContext(ApplicationConfig.class);

        Human human1 = ctx.getBean(&quot;human1&quot;, Human.class);
        System.out.println(&quot;이름 : &quot; + human1.getName());
        System.out.println(&quot;나이 : &quot; + human1.getAge());

        ctx.close();
    }
}</code></pre><p>  <strong>ApplicationConfig.java</strong></p>
<pre><code>  @Configuration
public class ApplicationConfig {

    @Bean
    public Human human1() {
        //생성자 함수이기 때문에 다음과 같이 값을 넣어준다.
        Human human = new Human(&quot;희곤&quot;, 20);
        //return 객체명은 똑같이 필요하다. 
        return human;
    }
}</code></pre><h3 id="✅✅-어노테이션-방식3-arraylist-같은-여러-값을-넣어주어야-하는-경우">✅✅ 어노테이션 방식3 (ArrayList 같은 여러 값을 넣어주어야 하는 경우)</h3>
<p>  <strong>Human.java</strong></p>
<pre><code>  public class Human {

        //필드 3개 (name, age, hobbys)
        private String name;
        private int age;
        private ArrayList&lt;String&gt; hobbys;

        //생성자함수
        public Human(String name, int age) {
            this.name = name;
            this.age = age;
        }

        //setter, getter
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }

        public ArrayList&lt;String&gt; getHobbys() {
            return hobbys;
        }

        public void setHobbys(ArrayList&lt;String&gt; hobbys) {
            this.hobbys = hobbys;
        }

}</code></pre><p>  <strong>MainClass.java</strong></p>
<pre><code>  public class MainClass {
    public static void main(String[] args) {

        AnnotationConfigApplicationContext ctx
        = new AnnotationConfigApplicationContext(ApplicationConfig.class);

        Human human1 = ctx.getBean(&quot;human1&quot;, Human.class);
        System.out.println(&quot;이름 : &quot; + human1.getName());
        System.out.println(&quot;나이 : &quot; + human1.getAge());
        System.out.println(&quot;취미 : &quot; + human1.getHobbys());

        ctx.close();
    }
}</code></pre><p>  <strong>ApplicationConfig.java</strong></p>
<p>  여러 값을 넣어주어야 하는 경우에는 데이터타입의 객체를 새롭게 만들고, .add() 함수 안에 값을 넣어주어 추가해준다. 그리고 setter 메소드에 객체를 넣어서 값을 넣어주어야 한다. (생성자 함수라면 생성자 함수 소괄호 안에 넣어주면 된다.)</p>
<pre><code>  @Configuration
public class ApplicationConfig {

    @Bean
    public Human human1() {
        //생성자 함수이기 때문에 다음과 같이 값을 넣어준다.
        Human human = new Human(&quot;희곤&quot;, 20);
        //ArrayList 와 같이 여러 값을 넣어주어야 하는 경우
        ArrayList&lt;String&gt; hobbys = new ArrayList&lt;String&gt;();
        hobbys.add(&quot;축구&quot;);
        hobbys.add(&quot;배틀그라운드&quot;);
        human.setHobbys(hobbys);
        //return 객체명은 똑같이 필요하다. 
        return human;
    }
}</code></pre><p>  ...끝!</p>
]]></description>
        </item>
    </channel>
</rss>