<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>새싹 코더의 TIL</title>
    <link>https://jg-han.tistory.com/</link>
    <description>하다보면 늘겠지.</description>
    <language>ko</language>
    <pubDate>Wed, 15 Apr 2026 17:14:36 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>걸륜</managingEditor>
    <image>
      <title>새싹 코더의 TIL</title>
      <url>https://tistory1.daumcdn.net/tistory/3967299/attach/4973f2c08a3b44ba96bfd958407341e5</url>
      <link>https://jg-han.tistory.com</link>
    </image>
    <item>
      <title>[SpringBoot]의존성 주입 과 의존성 역전</title>
      <link>https://jg-han.tistory.com/137</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;의존, 의존성이란?&lt;br /&gt;-&amp;gt; A는 B를 사용하기만 해도 A는 B에 의존한다 할 수 있다.&lt;br /&gt;- ex) new() 쓰는거&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;의존성 주입 : DI(Dependency Injection)&lt;/b&gt;&lt;br /&gt;- 의존성을 약화시키는 테크닉&lt;br /&gt;- 필요한 값을 new해서 인스턴스화 하는게 아니라 외부에서 넣어주는거&lt;br /&gt;- new() 는 사실상 하드코딩이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1787&quot; data-origin-height=&quot;741&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WS0aN/btsFpQKrzy5/kloxqHbw22rQXn2iVt54sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WS0aN/btsFpQKrzy5/kloxqHbw22rQXn2iVt54sK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WS0aN/btsFpQKrzy5/kloxqHbw22rQXn2iVt54sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWS0aN%2FbtsFpQKrzy5%2FkloxqHbw22rQXn2iVt54sK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1787&quot; height=&quot;741&quot; data-origin-width=&quot;1787&quot; data-origin-height=&quot;741&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;의존성 역전(Dependency Inversion)&lt;br /&gt;&lt;/b&gt;- DIP라고 부른다.&lt;br /&gt;- 화살표의 방향을 바꾸는 테크닉이라고 보면 된다.&lt;br /&gt;- 의존성 역전은 상위 모듈과 하위 모듈 모두 추상화에 의존해야 한다.&lt;br /&gt;- 세부사항에 의존해서는 안되고 세부 사항이 추상화에 의존해야 한다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2461&quot; data-origin-height=&quot;759&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCVJrX/btsFnWqSFuK/6wbXO0IQHKA7BAnPT4h2fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCVJrX/btsFnWqSFuK/6wbXO0IQHKA7BAnPT4h2fk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCVJrX/btsFnWqSFuK/6wbXO0IQHKA7BAnPT4h2fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCVJrX%2FbtsFnWqSFuK%2F6wbXO0IQHKA7BAnPT4h2fk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2461&quot; height=&quot;759&quot; data-origin-width=&quot;2461&quot; data-origin-height=&quot;759&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스와 구현을 분리&amp;nbsp; / 화살표 방향이 바뀜 -&amp;gt; 의존성을역전&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/SpringBoot</category>
      <author>걸륜</author>
      <guid isPermaLink="true">https://jg-han.tistory.com/137</guid>
      <comments>https://jg-han.tistory.com/137#entry137comment</comments>
      <pubDate>Sun, 3 Mar 2024 21:01:10 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] 오버로딩, 오버라이딩</title>
      <link>https://jg-han.tistory.com/136</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;1. 오버라이딩의 조건&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;자손클래스에서 오버라이딩하는 메서드는 조상 클래스의 메서드와&lt;br /&gt;- 이름이 같아야 한다.&amp;nbsp;&lt;br /&gt;- 매개변수가 같아야 한다.&lt;br /&gt;- 반환타입이 같아야 한다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;nbsp;조상 클래스의 메서드를 자손 클래스에서 오버라이딩 할때&lt;br /&gt;1. 접근 제어자를 조상 클래스의 메서드 보다 좁은 범위로 변경할 수 없다.&lt;br /&gt;2. 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다. 혹은 상우의 예외(Exception)으로 선언할 수 없다.&amp;nbsp;&lt;br /&gt;3. 인스턴스메서드를 static메서드로 또는 그 반대로 변경할 수 없다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 오버로딩 vs 오버라이딩&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오버로딩: 기존에 없는 새로운 메서드를 정의하는 것&lt;br /&gt;오버라이딩: 상속받은 메서드의 내용을 변경하는 것&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처: 자바의 정석&lt;/p&gt;</description>
      <category>Study/JAVA</category>
      <author>걸륜</author>
      <guid isPermaLink="true">https://jg-han.tistory.com/136</guid>
      <comments>https://jg-han.tistory.com/136#entry136comment</comments>
      <pubDate>Sat, 5 Nov 2022 17:25:09 +0900</pubDate>
    </item>
    <item>
      <title>[문제해결] Web server failed to start. Port 000 was already in use 에러</title>
      <link>https://jg-han.tistory.com/135</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k1WSA/btrPbDguTe6/XF8412EGiTDofZig7Ig4kK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k1WSA/btrPbDguTe6/XF8412EGiTDofZig7Ig4kK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k1WSA/btrPbDguTe6/XF8412EGiTDofZig7Ig4kK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk1WSA%2FbtrPbDguTe6%2FXF8412EGiTDofZig7Ig4kK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;986&quot; height=&quot;274&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;274&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무슨 이유인지는 모르겠지만 &lt;span style=&quot;background-color: #ffffff; color: #484848;&quot;&gt;포트가 이미 실행 중일 때 스프링을 Run 하면 발생한 에러라고 한다..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결방법&lt;br /&gt;&lt;/b&gt;(윈도우)&lt;br /&gt;cmd에서 netstat -ano&lt;span style=&quot;background-color: #ffffff; color: #484848;&quot;&gt;를 실행 하면 현재 실행중인 포트 목록이 나온다.&lt;br /&gt;여기서 8020포트에 해당하는 PID를 찾아서&lt;br /&gt;taskkill/pid 0000 / f&amp;nbsp;&lt;br /&gt;을 입력한다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #484848;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kEFs2/btrPbbj7gXi/ap09j5O9b3f36L4AgcE2Qk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kEFs2/btrPbbj7gXi/ap09j5O9b3f36L4AgcE2Qk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kEFs2/btrPbbj7gXi/ap09j5O9b3f36L4AgcE2Qk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkEFs2%2FbtrPbbj7gXi%2Fap09j5O9b3f36L4AgcE2Qk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;681&quot; height=&quot;502&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;502&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Study/SpringBoot</category>
      <author>걸륜</author>
      <guid isPermaLink="true">https://jg-han.tistory.com/135</guid>
      <comments>https://jg-han.tistory.com/135#entry135comment</comments>
      <pubDate>Fri, 21 Oct 2022 00:23:34 +0900</pubDate>
    </item>
    <item>
      <title>[백준/java] 1978번 문제 소수 찾기</title>
      <link>https://jg-han.tistory.com/134</link>
      <description>&lt;pre id=&quot;code_1656290089285&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.Scanner;

public class Main{

    public static boolean is_Prime(int n) {

        if(n == 1) {
            return false;
        }

        for(int i =2; i&amp;lt; n; i++) {
            if (n % i == 0) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int cnt = 0;
        for(int i = 0; i&amp;lt;n; i++){
            int input = sc.nextInt();
            if(is_Prime(input) == true)
                cnt ++;
        }

        System.out.println(cnt);

    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>CodingTest/백준</category>
      <author>걸륜</author>
      <guid isPermaLink="true">https://jg-han.tistory.com/134</guid>
      <comments>https://jg-han.tistory.com/134#entry134comment</comments>
      <pubDate>Mon, 27 Jun 2022 09:43:18 +0900</pubDate>
    </item>
    <item>
      <title>[스프링부트 개념정리] JPA의 특징(내용 추가예정..)</title>
      <link>https://jg-han.tistory.com/133</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. JPA는 Java Persistance Api다.&lt;/b&gt;&lt;br /&gt;영속성은 데이터가 영구히 기록될 수 있도록 하는 것이다.&amp;nbsp;&lt;br /&gt;JPA는 즉 자바에 있는 데이터를 영구히 기록할 수 있느 환경을 제공하는 API다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. JPA는 ORM 기술이다.&lt;/b&gt;&lt;br /&gt;ORM(Object Relational Mapping): Object를 DB에 연결하는 방법론, &lt;br /&gt;자바 클래스를 만들어서 실행하면 DB에 테이블을 자동적으로 생성시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. JPA는 반복적인 CRUD 작업을 생략하게 해준다.&lt;br /&gt;&lt;/b&gt;기존 : 자바 프로그램이 DB에 커넥션을 요청 -&amp;gt; DB는 세션을 오픈 -&amp;gt; 오픈된 세션으로 자바 프로그램은 connection을 가짐 -&amp;gt; 다음 요청시에는 쿼리를 전송할 수 있음 -&amp;gt; DB는 쿼리를 기반으로 데이터를 만들고 자바에 응답 -&amp;gt; 응답한 데이터 타입과 자바의 데이터 타입이 다르므로 자바는 해당 데이터를 자바 object로 변경&lt;b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;하지만 JPA 사용하면 전송된 쿼리에 대한 응답이 있을때 응답을 자바 오브젝트로 바꾸고 연결된 세션, 커넥션을 끊는 일련의 작업을 단순한 함수로 제공해준다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/SpringBoot</category>
      <author>걸륜</author>
      <guid isPermaLink="true">https://jg-han.tistory.com/133</guid>
      <comments>https://jg-han.tistory.com/133#entry133comment</comments>
      <pubDate>Wed, 22 Jun 2022 22:00:01 +0900</pubDate>
    </item>
    <item>
      <title>[스프링부트 개념정리] 어노테이션, 리플렉션</title>
      <link>https://jg-han.tistory.com/132</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;필터&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링컨테이너가 들고 있는 필터를 '인터셉터'라고 부른다.&lt;br /&gt;인터셉터 또한 권한을 체크한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;어노테이션 &amp;amp; 리플렉션&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;어노테이션&lt;/b&gt;&lt;br /&gt;어노테이션은 사전뜻대로는 주석이지만 스프링에서 어노테이션은 컴파일러가 무시하지 않고 체킹을 한다.&lt;br /&gt;스프링은 어노테이션으로 객체를 생성하기도 한다.&amp;nbsp;&lt;br /&gt;ex)&lt;br /&gt;@Component: 해당 어노테이션이 붙어있는 클래스를 메모리에 로딩해&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;특정 클래스에 해당 어노테이션이 붙어있으면 스프링은 그 클래스를 스캔해서&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;자기가 들고있는 메모리공간(heap영역, 컨테이너)에 로드한다. =&amp;gt; IOC&lt;br /&gt;@Autowired: 스프링 컨테이너에&amp;nbsp; 있는 클래스를 사용하고 싶을 때사용&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;리플렉션&lt;br /&gt;&lt;/b&gt;&amp;nbsp;- 스프링이 B클래스를 스캔할 때 B클래스 내부에 어떤 것들이 있는지 분석하는 기법을 리플랙션이라 한다.&lt;br /&gt;&lt;b&gt;&lt;/b&gt;&amp;nbsp;- 리플랙션은 &lt;b&gt;어떤 메서드, 필드, 어노테이션이 있는지를 체킹&lt;/b&gt;한다.&lt;br /&gt;&amp;nbsp; &amp;nbsp;또한 있는지를 체킹하는것을 넘어서 어떤 동작을 할지를 설정할수도 있다.&amp;nbsp;&lt;br /&gt;&amp;nbsp;- 만약 @Autowired 를 발견하면 스프링 컨테이너를 쭉 읽어어서 A랑 동일한 타입의 객체가 있는지를 확인한다.&lt;br /&gt;&amp;nbsp; 만약 A가 없다면 null을 반환, 있다면 A를 DI한다.&amp;nbsp;&amp;nbsp;&lt;br /&gt;- 리플렉션은 &lt;b&gt;런타임때 동작(분석&lt;/b&gt;)한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655384547692&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Class B{
  @Autowired
  A a;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/SpringBoot</category>
      <author>걸륜</author>
      <guid isPermaLink="true">https://jg-han.tistory.com/132</guid>
      <comments>https://jg-han.tistory.com/132#entry132comment</comments>
      <pubDate>Thu, 16 Jun 2022 22:05:30 +0900</pubDate>
    </item>
    <item>
      <title>[스프링부트 개념정리] Ioc, DI란</title>
      <link>https://jg-han.tistory.com/131</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Inversion of Control(제어의 역전)&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 주도권이 스프링에게 있다.&lt;br /&gt;- 클래스: 설계도&lt;br /&gt;- Object: 실체화가 가능한 것&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;ex) 롤 챔프 누누(Class)는 게임할때 튀어나오니까 실체화가 가능하다 =&amp;gt; Object다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;하지만 캐릭터(abstract Class)는 추상적인 의미다. (애쉬가 될수도 있고, 케이틀린이 될수도 있다.)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;캐릭터는 추상적인 것이기때문에 실체화가 불가능하다, 게임에서 등장 불가능 =&amp;gt; Object가 아니다.&lt;br /&gt;- Instance: 실체화가 된것 (누누가 전장에서 돌아다니면, 게임속에서 존재하는것)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주도권이 스프링에게 있다는것은 &lt;br /&gt;개발자가 직접 Object를 new해서 heap 공간에 올리게 되면 해당 레퍼런스의 주소를 각각의 메소드가 관리하게된다.&lt;br /&gt;예를 들어보자&lt;/p&gt;
&lt;pre id=&quot;code_1655305142257&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Public void make(){
	User user = new User(); 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예컨데 user의 주소는 make() 메소드가 실행되는 순간에 메모리에 뜬다.&lt;br /&gt;그래서 다른 메소드에서 user를 사용하고 싶다면 아래와 같이 또 새로 new해야할 텐데 그럼&amp;nbsp;&lt;br /&gt;heap메모리에 또다른&amp;nbsp; 주소가 생기게 된다.&amp;nbsp;&lt;br /&gt;또한 make메소드에서의 user와 make2 메소드에서의user는 다른 존재다.(주소가 다르기 때문)&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655305283802&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Public void make2(){
	User user = new User(); 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 다른 매소드에서 같은 user를 공유를 하기 힘들어진다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;스프링는 수많은 Object들을 스캔해서 그 객체들을 heap 메모리 공간에 올려준다.&lt;br /&gt;이것을 IOC라고한다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;DI란&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;스프링이 IOC를 통해 Object들을 메모리에 띄웠기 때문에, 스프링이 관리하기 때문에&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;스프링이 관리하는 객체들을 개발자가 원하는 모든곳(메소드)에서 가져와 쓸 수 있는것을 말한다.&amp;nbsp;&lt;br /&gt;make 메소드의 user나, make2 메소드에서의 user는 같은 user다.&lt;br /&gt;한마디로 싱글톤으로 관리된다. =&amp;gt; 스프링이 오브젝트를 스캔하면 user가 딱 한번 힙메모리에 뜨고 그것을 다양한 곳에서 공유해서 쓴다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;한줄 요약&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; letter-spacing: 0px;&quot;&gt;Ioc: 제어의역전, 스프링이 싱글톤으로 빈을 관리하는것&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666; letter-spacing: 0px;&quot;&gt;Di: 인스턴스 생성시 IOC 컨터에너에서 싱글톤으로 관리하는 빈들을 변수에 의존성 주입&lt;/span&gt;&lt;/p&gt;</description>
      <category>Study/SpringBoot</category>
      <author>걸륜</author>
      <guid isPermaLink="true">https://jg-han.tistory.com/131</guid>
      <comments>https://jg-han.tistory.com/131#entry131comment</comments>
      <pubDate>Thu, 16 Jun 2022 00:23:33 +0900</pubDate>
    </item>
    <item>
      <title>[Springboot] AOP처리 - 유효성검사</title>
      <link>https://jg-han.tistory.com/130</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;공통기능을 필터링처리 하면 코드를 재활용해서 훨씬 코드를 깔끔하게 쓸 수 있다.&amp;nbsp;&lt;br /&gt;이 공통기능을 AOP처리할거다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;AOP기능 구현에 앞서 dependency 를 추가해줘야한다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop
implementation 'org.springframework.boot:spring-boot-starter-aop:2.4.5'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ValidationAdvice&lt;/b&gt;&lt;br /&gt;- 유효성 검사를 공통으로 처리하는 곳&lt;br /&gt;- @Aspect가 있어야 AOP를 처리할 수 있는 핸들러가 된다.&amp;nbsp;&lt;br /&gt;- api쪽 advice와 그냥 advice를 만든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&lt;br /&gt;@Before(): 특정 함수 실행직전에 실행 &lt;br /&gt;@After(): 특정 함수 실행 직후에 실행 &lt;br /&gt;@Around(): 특정 함수 실행직전부터 직후에 관여&lt;br /&gt;&lt;br /&gt;2.&lt;br /&gt;-&amp;nbsp; ....web.api의 모든 컨트롤러의 모든 메소드의 모든 매개변수 가 실행될때 작동된다.&lt;br /&gt;- &lt;span&gt;proceedingJoinPoint&lt;span&gt; : api의 Controller중에 특정 함수가 실행됐으면&amp;nbsp; proceedingJoinPoint가&lt;br /&gt;그 함수의 매개변수 뿐만 아니라 내부의 모든 정보에 접근할 수 있다.&lt;br /&gt;- 예를들어 profile함수가 실행되는 순간 그 함수의 모든 정보를&amp;nbsp; proceedingJoinPoint가 담고&lt;b&gt; profile함수 실행 이전에 apiAdvice가 먼저 실행된다.&amp;nbsp;&lt;br /&gt;&lt;/b&gt;- 그 이후 return proceedingJoinPoint.proceed(); 가 실행되며 profile함수가 실행된다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.&amp;nbsp;&lt;br /&gt;proceedingJoinPoint.getArgs() 를 통해 함수의 매개변수에 접근해서 리스트에 담는다.&amp;nbsp;&lt;br /&gt;만약에 매개변수 중에서 BindingResult라는 타입이 있으면&amp;nbsp;&lt;br /&gt;-&amp;gt; 해당 arg를 BindingResult형으로 다운 캐스팅&lt;br /&gt;-&amp;gt; 만약 bindingResult에 에러가 있다면 -&amp;gt; 유효성 검사 실행&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Component
@Aspect
public class ValidationAdvice {

    @Around(&quot;execution(* com.jghan.instaclone.web.api.*Controller.*(..))&quot;)
    public Object apiAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

//        System.out.println(&quot;==================web api 컨트롤러===================&quot;);

        Object[] args = proceedingJoinPoint.getArgs();
        for(Object arg: args){
            if(arg instanceof BindingResult){
                BindingResult bindingResult = (BindingResult) arg;
                if (bindingResult.hasErrors()) {
                    Map&amp;lt;String, String&amp;gt; errorMap = new HashMap&amp;lt;&amp;gt;();

                    for (FieldError error : bindingResult.getFieldErrors()) {
                        errorMap.put(error.getField(), error.getDefaultMessage());
                    }
                    throw new CustomValidationApiException(&quot;유효성 검사 실패함&quot;, errorMap);
                }
            }
        }
        return proceedingJoinPoint.proceed(); //prfofile함수가 실행됨
    }

    @Around(&quot;execution(* com.jghan.instaclone.web.*Controller.*(..))&quot;)
    public Object advice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        Object[] args = proceedingJoinPoint.getArgs();
        for(Object arg: args){
            if(arg instanceof BindingResult){
                BindingResult bindingResult = (BindingResult) arg;

                if (bindingResult.hasErrors()) {
                    Map&amp;lt;String, String&amp;gt; errorMap = new HashMap&amp;lt;&amp;gt;();

                    for (FieldError error : bindingResult.getFieldErrors()) {
                        errorMap.put(error.getField(), error.getDefaultMessage());

                    }
                    throw new CustomValidationException(&quot;유효성 검사 실패함&quot;, errorMap);
                }

            }
        }

        return proceedingJoinPoint.proceed();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/SpringBoot</category>
      <author>걸륜</author>
      <guid isPermaLink="true">https://jg-han.tistory.com/130</guid>
      <comments>https://jg-han.tistory.com/130#entry130comment</comments>
      <pubDate>Fri, 3 Jun 2022 14:04:30 +0900</pubDate>
    </item>
    <item>
      <title>[Springboot] 댓글기능 구현</title>
      <link>https://jg-han.tistory.com/129</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Comment.class&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Comment 와 User는 n:1관계다 (1명의 유저는 여러개의 댓글을 쓸 수 있으므로&lt;br /&gt;- 또한 하나의 이미지에 여러 댓글이 달릴 수 있으므로 Comment 와 Image는 n:1이다.&lt;b&gt;&lt;br /&gt;- ManyToOne에서는 EAGER 가 디폴트 &lt;br /&gt;&lt;/b&gt;&amp;nbsp; =&amp;gt;왜냐하면 1개 댓글에는 user나 image 정보가 1개뿐이라서 join해서 가져와도 db에 무리가 없다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class Comment {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(length = 100, nullable = false)
    private String content;

    @JsonIgnoreProperties(&quot;{images}&quot;)
    @JoinColumn(name = &quot;userId&quot;)
    @ManyToOne(fetch =FetchType.EAGER) 
    private User user;

    @JoinColumn(name = &quot;imageId&quot;)
    @ManyToOne(fetch = FetchType.EAGER)
    private Image image;


    private LocalDateTime createDate;

    @PrePersist
    public void createDate(){
        this.createDate = LocalDateTime.now();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CommentService.class&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@RequiredArgsConstructor
@Service
public class CommentService {

    private final CommentRepository commentRepository;
    private final UserRepository userRepository;

    @Transactional
    public Comment commentSave(String content, int imageId, int userId){

        Image image = new Image();
        image.setId(imageId);
        
        User userEntity = userRepository.findById(userId).orElseThrow(()-&amp;gt;{
            return new CustomApiException(&quot;유저 아이디를 찾을 수 없습니다.&quot;);
        });

        Comment comment = new Comment();
        comment.setContent(content);
        comment.setImage(image);
        comment.setUser(userEntity);

        return commentRepository.save(comment);


}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CommentApiController.class&lt;br /&gt;&lt;/b&gt;- CommentDto commentDto만 쓰는건 x-www-urlencoded 로 받는거기 때문에&lt;br /&gt;&amp;nbsp; &amp;nbsp; json 데이터 받으려면&amp;nbsp;&amp;nbsp;@RequestBody&amp;nbsp;&amp;nbsp;붙여줘야한다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@RequiredArgsConstructor
@RestController
public class CommentApiController {

    private final CommentService commentService;

    @PostMapping(&quot;/api/comment&quot;)
    public ResponseEntity&amp;lt;?&amp;gt; commentSave(@RequestBody CommentDto commentDto,
                                         @AuthenticationPrincipal PrincipalDetails principalDetails){

        Comment comment = commentService.commentSave(commentDto.getContent(), commentDto.getImageId(), principalDetails.getUser().getId());
        return new ResponseEntity&amp;lt;&amp;gt;(new CMRespDto&amp;lt;&amp;gt;(1, &quot;댓글쓰기성공&quot;, comment), HttpStatus.CREATED);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;story.js&lt;/b&gt;&lt;br /&gt;&amp;nbsp; - 댓글을 달고자하는 이미지의 id를 전달해줘서 클릭시 addComment() 함수가 실행되도록 한다.&lt;br /&gt;- 댓글 작성하고 DB에 댓글이 저장되고, 그 내용이 storyCommentList에 append되야한다&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;...

&amp;lt;div id=&quot;storyCommentList-${image.id}&quot;&amp;gt;`;

                image.comments.forEach((comment)=&amp;gt;{
                    item +=`&amp;lt;div class=&quot;sl__item__contents__comment&quot; id=&quot;storyCommentItem-${comment.id}&quot;&amp;gt;
                    &amp;lt;p&amp;gt;
                        &amp;lt;b&amp;gt; ${image.user} :&amp;lt;/b&amp;gt; ${comment.content}
                    &amp;lt;/p&amp;gt;

                    &amp;lt;button onclick=&quot;deleteComment(${comment.id})&quot;&amp;gt;
                        &amp;lt;i class=&quot;fas fa-times&quot;&amp;gt;&amp;lt;/i&amp;gt;
                    &amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;`;

                });


            item += `
         &amp;lt;/div&amp;gt;

         &amp;lt;div class=&quot;sl__item__input&quot;&amp;gt;
			&amp;lt;input type=&quot;text&quot; placeholder=&quot;댓글 달기...&quot; id=&quot;storyCommentInput-${image.id}&quot; /&amp;gt;
             &amp;lt;button type=&quot;button&quot; onClick=&quot;addComment(${image.id} )&quot;&amp;gt;게시&amp;lt;/button&amp;gt;
         &amp;lt;/div&amp;gt;

     &amp;lt;/div&amp;gt;
     &amp;lt;/div&amp;gt;`;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function addComment(imageId) {

   let commentInput = $(`#storyCommentInput-${imageId}`);

       let commentList = $(`#storyCommentList-${imageId}`);

       let data = {
           imageId: imageId,
          content: commentInput.val()
    };

   if (data.content === &quot;&quot;) {
      alert(&quot;댓글을 작성해주세요!&quot;);
      return;
   }

   $.ajax({
       type:&quot;post&quot;,
       url:&quot;/api/comment&quot;,
       data:JSON.stringify(data),
        contentType: &quot;application/json; charset=utf-8&quot;,
        dataType: &quot;json&quot;
   }).done(res=&amp;gt;{
       console.log(&quot;성공&quot;, res);

        let comment = res.data;

        console.log(comment);

        let content = `
          &amp;lt;div class=&quot;sl__item__contents__comment&quot; id=&quot;storyCommentItem-${comment.id}&quot;&amp;gt;
            &amp;lt;p&amp;gt;
              &amp;lt;b&amp;gt;${comment.user.username} :&amp;lt;/b&amp;gt;
              ${comment.content}
            &amp;lt;/p&amp;gt;
            &amp;lt;button&amp;gt;&amp;lt;i class=&quot;fas fa-times&quot;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/button&amp;gt;
          &amp;lt;/div&amp;gt;
        `;
        commentList.prepend(content);
   }).fail(error=&amp;gt;{
       console.log(&quot;오류&quot;, error);
   });

   commentInput.val(&quot;&quot;); //인풋필드를 깨끗하게 비워준다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CommentDto&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- addComment()가 전달하는 값이 imageId와 content 뿐이기에 이 데이터를 받는 dto가 필요하다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;@Data
public class CommentDto {
    private String content;
    private int imageId;

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ImageApiController의 imageStory 함수가 등록한 댓글을 뿌려줘야 하는데,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;835&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9IZrk/btrDc7OC6uV/l37UGMWJ4niDXM4daGaZM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9IZrk/btrDc7OC6uV/l37UGMWJ4niDXM4daGaZM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9IZrk/btrDc7OC6uV/l37UGMWJ4niDXM4daGaZM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9IZrk%2FbtrDc7OC6uV%2Fl37UGMWJ4niDXM4daGaZM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;702&quot; height=&quot;279&quot; data-origin-width=&quot;835&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Image.class&lt;/b&gt;&lt;br /&gt;현재 Image 객체에는 댓글관련 정보를 들고 있지 않기 때문에 관련 컬럼을 만들어주고, 양방향 매핑을 해줘야한다.&amp;nbsp;&lt;br /&gt;- 한 이미지에 여러 comments가 붙기때문에 OneToMany 어노테이션을 붙여준다.&lt;br /&gt;-&amp;nbsp; 이미지를 가져올때 Comment의 이미지 정보를 가져올 필요가 없으므로 JsonIgnore해준다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class Image {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

   ...

    //댓글
    @OrderBy(&quot;id DESC&quot;)
    @JsonIgnoreProperties({&quot;image&quot;})
    @OneToMany(mappedBy = &quot;image&quot;)
    private List&amp;lt;Comment&amp;gt;comments;

   ...
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Study/SpringBoot</category>
      <author>걸륜</author>
      <guid isPermaLink="true">https://jg-han.tistory.com/129</guid>
      <comments>https://jg-han.tistory.com/129#entry129comment</comments>
      <pubDate>Wed, 1 Jun 2022 11:45:17 +0900</pubDate>
    </item>
    <item>
      <title>[Springboot] 프로필 사진 변경</title>
      <link>https://jg-han.tistory.com/128</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;profile.html&lt;br /&gt;&lt;/b&gt;- profile-img-wrap story-border 를 클릭했을때 popup 메소드가 호출되면서 modal-image 가 팝업된다.&lt;br /&gt;- 이 modal-image의&amp;nbsp; '사진업로드'를 클릭하면 profileImageUpload() 함수가 호출된다.&lt;br /&gt;- profileImageUpload 함수에 페이지 유저의 아이디와 로그인 유저 아이디를 전달한다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;...
&amp;lt;div class=&quot;profile-left&quot;&amp;gt;
  &amp;lt;div class=&quot;profile-img-wrap story-border&quot;
       onclick=&quot;popup('.modal-image')&quot;&amp;gt;

    &amp;lt;form id=&quot;userProfileImageForm&quot;&amp;gt;
      &amp;lt;input type=&quot;file&quot; name=&quot;profileImageFile&quot; style=&quot;display: none;&quot;
             id=&quot;userProfileImageInput&quot; /&amp;gt;
    &amp;lt;/form&amp;gt;

    &amp;lt;img class=&quot;profile-image&quot; src=&quot;&quot; th:src=&quot;*{profileImageUrl}&quot;
         onerror=&quot;this.src='/images/person.jpeg'&quot; id=&quot;userProfileImage&quot; /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
...


&amp;lt;!--프로필사진 바꾸기 모달--&amp;gt;
&amp;lt;div class=&quot;modal-image&quot; onclick=&quot;modalImage()&quot;&amp;gt;
  &amp;lt;div class=&quot;modal&quot;&amp;gt;
    &amp;lt;p&amp;gt;프로필 사진 바꾸기&amp;lt;/p&amp;gt;
    &amp;lt;button onclick=&quot;profileImageUpload()&quot; th:onclick=&quot;|profileImageUpload('${dto.user.id}','${#authentication.principal.user.id}')|&quot;&amp;gt;사진 업로드&amp;lt;/button&amp;gt;
    &amp;lt;button onclick=&quot;closePopup('.modal-image')&quot;&amp;gt;취소&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;profile.js&lt;br /&gt;&lt;/b&gt;- 페이지주인 아이디와 로그인 유저 아이드를 받는다.&lt;br /&gt;- 페이지 유저 아이디랑 로그인 유저가 동일하다면 자동으로 userProfileImageInput 부분을 클릭시킨다.&lt;br /&gt;1. 서버에 이미지 전송&lt;br /&gt;- userProfileImageForm의 0번째 요소를 찾아서 &amp;nbsp;profileImageForm변수에 넣어준다.&amp;nbsp;&lt;br /&gt;- ajax로 이 데이터를 전송하려면 FormData로 감싸야 한다.&amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp;=&amp;gt;&lt;span&gt;profileImageForm은 폼테그 그 자체고그것을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;FormData에 넣으면 값들만 담긴다고 생각하면 된다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;function profileImageUpload(pageUserId, principalId) {

   if(pageUserId != principalId){
      alert(&quot;프로필 사진을 수정할 수 없는 유저입니다.&quot;);
      return;
   }

   $(&quot;#userProfileImageInput&quot;).click();

   $(&quot;#userProfileImageInput&quot;).on(&quot;change&quot;, (e) =&amp;gt; {
      let f = e.target.files[0];

      if (!f.type.match(&quot;image.*&quot;)) {
         alert(&quot;이미지를 등록해야 합니다.&quot;);
         return;
      }

      // 서버에 이미지를 전송
      let profileImageForm = $(&quot;#userProfileImageForm&quot;)[0];
      console.log(profileImageForm);

      // FormData 객체를 이용하면 form 태그의 필드와 그 값을 나타내는 일련의 key/value 쌍을 담을 수 있다.
      let formData = new FormData(profileImageForm);

      $.ajax({
         type: &quot;put&quot;,
         url: `/api/user/${principalId}/profileImageUrl`,
         data: formData,
         contentType: false, // 필수 : x-www-form-urlencoded로 파싱되는 것을 방지
         processData: false,  // 필수: contentType을 false로 줬을 때 QueryString 자동 설정됨. 해제
         enctype: &quot;multipart/form-data&quot;,
         dataType: &quot;json&quot;
      }).done(res=&amp;gt;{
         // 사진 전송 성공시 이미지 변경
         let reader = new FileReader();
         reader.onload = (e) =&amp;gt; {
            $(&quot;#userProfileImage&quot;).attr(&quot;src&quot;, e.target.result);
         }
         reader.readAsDataURL(f); // 이 코드 실행시 reader.onload 실행됨.
      }).fail(error=&amp;gt;{
         console.log(&quot;오류&quot;, error);
      });


   });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UserApiController&lt;br /&gt;&lt;/b&gt;- 사진데이터도 받기 때문에 MultipartFile도 받는데, html에서의 input 아이디와 똑같은 이름으로 받아야한다.&amp;nbsp;&lt;br /&gt;- userService에 principalId와 파일정보를 넘긴다.&lt;br /&gt;- 회원사진이 변경되면 세션값이 변경되야하기 때문에 변경된 userEntity를 받아서 세션변경을 해준다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@RequiredArgsConstructor
@RestController
public class UserApiController {

    private final UserService userService;
    private final FollowService followService;


    @PutMapping(&quot;/api/user/{principalId}/profileImageUrl&quot;)
    public ResponseEntity&amp;lt;?&amp;gt; profileImageUrlUpdate(@PathVariable int principalId, MultipartFile profileImageFile,
                                                   @AuthenticationPrincipal PrincipalDetails principalDetails){
        User userEntity = userService.profileImageUrlUpdate(principalId, profileImageFile);
        principalDetails.setUser(userEntity); // 세션 변경
        return new ResponseEntity&amp;lt;&amp;gt;(new CMRespDto&amp;lt;&amp;gt;(1, &quot;프로필사진변경 성공&quot;, null), HttpStatus.OK);
    }

..


}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UserService&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;
    private final FollowRepositoy followRepositoy;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    @Value(&quot;${file.path}&quot;) //application.properties에서 가져옴
    private String uploadFolder;

    @Transactional
    public User profileImageUrlUpdate(int principalId, MultipartFile profileImageFile){

        UUID uuid = UUID.randomUUID(); // uuid
        String imageFileName = uuid+&quot;_&quot;+profileImageFile.getOriginalFilename(); // 1.jpg
        System.out.println(&quot;이미지 파일이름 : &quot;+imageFileName);

        Path imageFilePath = Paths.get(uploadFolder+imageFileName);

        try {
            Files.write(imageFilePath, profileImageFile.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }

        User userEntity = userRepository.findById(principalId).orElseThrow(()-&amp;gt;{
            // throw -&amp;gt; return 으로 변경
            return new CustomApiException(&quot;유저를 찾을 수 없습니다.&quot;);
        });
        userEntity.setProfileImageUrl(imageFileName);

        return userEntity;

    }&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Study/SpringBoot</category>
      <author>걸륜</author>
      <guid isPermaLink="true">https://jg-han.tistory.com/128</guid>
      <comments>https://jg-han.tistory.com/128#entry128comment</comments>
      <pubDate>Tue, 31 May 2022 11:11:24 +0900</pubDate>
    </item>
  </channel>
</rss>