<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>YEAHx4 Dev</title>
    <link>https://yeahx4.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 14 Apr 2026 05:48:25 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>YEAHx4</managingEditor>
    <image>
      <title>YEAHx4 Dev</title>
      <url>https://tistory1.daumcdn.net/tistory/6684993/attach/590d7ac61af34cc4a024127e80d9e32c</url>
      <link>https://yeahx4.tistory.com</link>
    </image>
    <item>
      <title>[Spring Boot] Entity builder 설정하기</title>
      <link>https://yeahx4.tistory.com/14</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot에서 Entity를 만들면 id와 다른 여러 필드로 구성한다. repository에 save 할 때 Entity의 instance를 만들어야 하는데 Lombok의 @AllArgsConstructor를 통해서 객체를 만들 수 있지만 필드의 순서나 변경이 생기면 관련 로직이 모두 망가지는 문제가 있다. 또, id같이 직접 지정하지 않아도 되는 필드에 임의로 값을 넣어야 하는 불편함도 있다. JPA에 의해서 자동으로 id값이 변경되어 저장되긴 하지만 의도하지 않은 동작을 일으킬 우려가 있다. Lombok에서 제공하는 @Builder를 사용하면 이런 문제를 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script&gt;  if (window.location.pathname.split(&quot;/&quot;)[1] === &quot;m&quot; &amp;&amp; navigator.userAgent.indexOf(&quot;Tistory&quot;) === -1 &amp;&amp; navigator.userAgent.indexOf(&quot;Android&quot;) === -1) {    window.location.href = window.location.origin + window.location.pathname.substr(2);  }&lt;/script&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;User 엔티티&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자의 user 엔티티의 스키마는 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 121px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px; text-align: center;&quot;&gt;이름&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px; text-align: center;&quot;&gt;타입&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px; text-align: center;&quot;&gt;비고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;id&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;int&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;PRIMARY KEY&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;email&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;String&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;handle&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;String&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;username&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;String&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;password&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;String&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;accountType&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;AccountType&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;enum&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Builder 생성자 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;builder에 포함시키고 싶은 필드를 초기화하는 생성자를 만들고 @Builder를 붙여주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1705254746573&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Builder
public User(String email, String handle, String username, String password, AccountType type) {
    this.email = email;
    this.handle = handle;
    this.username = username;
    this.password = password;
    this.accountType = type;
}&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;/p&gt;
&lt;pre id=&quot;code_1705254795724&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;User user = User.builder()
    .email(&quot;test1@mail.com&quot;)
    .handle(&quot;handle&quot;)
    .username(&quot;User1&quot;)
    .password(&quot;password&quot;)
    .build();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본값 지정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 보면 accountType를 지정하지 않은 것을 볼 수 있다. 미리 기본값을 지정해놓았기 때문인데, builder를 위한 기본값은 아래 조건을 만족하면 지정할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 필드를 포함하는 생성자를 가질 것 (AllArgsConstructor)&lt;/li&gt;
&lt;li&gt;엔티티 클래스에 Builder 어노테이션이 있을 것&lt;/li&gt;
&lt;li&gt;대상 필드에 Builder.Default 어노테이션과 기본값(초기값)이 있을 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드로 보면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1705255026725&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ...
@AllArgsConstructor
@Builder
public class User extends TimeEntity {
    // ...
    @Column(nullable = false)
    @Enumerated(EnumType.STRING)
    @Builder.Default
    private AccountType accountType = AccountType.OWN;
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 엔티티 객체를 만들 수 있는 AllArgsContructor나 NoArgsConstructor는 가능하면 노출하지 않는 것이 좋다. lombok에서는 생성자들의 visibility를 직접 설정할 수 있는데 protected 정도로 두면 필요에 따라 내부에서는 사용하면서도 외부에는 노출하지 않을 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1705255521430&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Spring Boot</category>
      <category>backend</category>
      <category>Builder</category>
      <category>Entity</category>
      <category>Java</category>
      <category>JPA</category>
      <category>Spring</category>
      <category>spring boot</category>
      <author>YEAHx4</author>
      <guid isPermaLink="true">https://yeahx4.tistory.com/14</guid>
      <comments>https://yeahx4.tistory.com/14#entry14comment</comments>
      <pubDate>Mon, 15 Jan 2024 03:05:50 +0900</pubDate>
    </item>
    <item>
      <title>[Spring Boot] 에러 메세지 커스터마이징 하기</title>
      <link>https://yeahx4.tistory.com/11</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 부트 3이전의 구버전에서는 ResponseEntityExceptionHandler을 상속해서 아래의 메소드를 오버라이딩하는 방식으로 접근할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script&gt;  if (window.location.pathname.split(&quot;/&quot;)[1] === &quot;m&quot; &amp;&amp; navigator.userAgent.indexOf(&quot;Tistory&quot;) === -1 &amp;&amp; navigator.userAgent.indexOf(&quot;Android&quot;) === -1) {    window.location.href = window.location.origin + window.location.pathname.substr(2);  }&lt;/script&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1702564719472&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Override
protected ResponseEntity&amp;lt;Object&amp;gt; handleMethodArgumentNotValid(
        MethodArgumentNotValidException ex,
        HttpHeaders headers,
        WebRequest request
) {
    ...
}&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;스프링 부트 3부터는 상속대신 어노테이션을 통해 접근하는 것으로 바뀌었지만 내부 구현 자체는 똑같다. 필자는 HTTP 응답을 일관적으로 data, message, status필드로 구성하고 있으므로 이 형식에 맞춰 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1702565467273&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    protected ResponseEntity&amp;lt;Object&amp;gt; handleMethodArgumentNotValid(
            MethodArgumentNotValidException ex,
            WebRequest request
    ) {
        Map&amp;lt;String, Object&amp;gt; body = new HashMap&amp;lt;&amp;gt;();
        List&amp;lt;String&amp;gt; errors = ex
                .getBindingResult()
                .getFieldErrors()
                .stream()
                .map(it -&amp;gt; it.getDefaultMessage())
                .toList();

        body.put(&quot;status&quot;, &quot;Error&quot;);
        body.put(&quot;data&quot;, null);
        body.put(&quot;message&quot;, errors);

        return new ResponseEntity&amp;lt;&amp;gt;(body, HttpStatus.BAD_REQUEST);
    }
}&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tr2au/btsBY37PlwU/KAXqOsbed18HUyLJxWwI30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tr2au/btsBY37PlwU/KAXqOsbed18HUyLJxWwI30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tr2au/btsBY37PlwU/KAXqOsbed18HUyLJxWwI30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTr2au%2FbtsBY37PlwU%2FKAXqOsbed18HUyLJxWwI30%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;478&quot; height=&quot;276&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;276&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;h3 data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 저 메세지는 dto의 validation에서 정의한 값이다.&lt;/p&gt;
&lt;pre id=&quot;code_1702565591104&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@NotBlank(message = &quot;Email Required&quot;)
@Email
@Size(max = 100)
String email,&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/개발일지</category>
      <author>YEAHx4</author>
      <guid isPermaLink="true">https://yeahx4.tistory.com/11</guid>
      <comments>https://yeahx4.tistory.com/11#entry11comment</comments>
      <pubDate>Thu, 14 Dec 2023 23:53:17 +0900</pubDate>
    </item>
    <item>
      <title>[Spring Boot] 값 검증하기 - Spring Validation</title>
      <link>https://yeahx4.tistory.com/9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 회원가입을 구현하기 위해서 필요한 정보를 dto로 전송받고 있었다.&lt;/p&gt;
&lt;pre id=&quot;code_1701669126643&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;signUp(@RequestBody SignUpDto dto) {&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1701669157176&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public record SignUpDto(
        String email,
        String handle,
        String username,
        String password
) {
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린에서는 String타입에 null이 들어갈 수 없기 때문에 모든 필드를 작성해야 하지만, 자바에서는 몇 필드가 없거나 null이어도 그대로 null값으로 전송된다. portpolent에서는 Entity의 설정에 nullable을 false로 해놨기 때문에 SQL Exception이 나고 결과적으로 500 Internal Error가 반환된다. 그렇다고 매 코드에 if를 쓰고 정규표현식을 써서 검사하기엔 반복되는 코드가 많고 지저분해진다. 그럴때 쓰는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script&gt;  if (window.location.pathname.split(&quot;/&quot;)[1] === &quot;m&quot; &amp;&amp; navigator.userAgent.indexOf(&quot;Tistory&quot;) === -1 &amp;&amp; navigator.userAgent.indexOf(&quot;Android&quot;) === -1) {    window.location.href = window.location.origin + window.location.pathname.substr(2);  }&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Validation을 사용하기 위해 의존성을 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1701670633242&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'org.springframework.boot:spring-boot-starter-validation'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Validation을 사용하면 요청을 받을 때 인자들을 검수하고 조건에 맞지 않으면 400 Bad Request로 종료시킨다. validation을 활성화 하려면 controller의 인자에 @Valid를 붙여주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1701670841484&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;signUp(@RequestBody @Valid SignUpDto dto) {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SignUpDto의 각 필드에 필요한 설정을 적용하면 끝이다.&lt;/p&gt;
&lt;pre id=&quot;code_1701671753498&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package io.yeahx4.portpolent.dto.user;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public record SignUpDto(
        @Email
        @Size(max = 100)
        @NotNull
        String email,

        @Size(min = 4, max = 20)
        @NotNull
        String handle,

        @Size(min = 2, max = 10)
        @NotNull
        String username,

        @NotNull
        String password
) {
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 쓰는 annotation&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NotNull : null이 아니여야 한다&lt;/li&gt;
&lt;li&gt;Size : min ~ max사이의 길이를 가져야 한다. 수의 크기와는 다르다.&lt;/li&gt;
&lt;li&gt;Min, Max : 숫자형의 최대, 최소를 설정한다.&lt;/li&gt;
&lt;li&gt;Email : 이메일 형태의 자료인지 확인한다.&lt;/li&gt;
&lt;li&gt;Pattern : 정규 표현식으로 검증한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발/개발일지</category>
      <category>backend</category>
      <category>Java</category>
      <category>portpolent</category>
      <category>Spring</category>
      <category>spring boot</category>
      <category>spring validation</category>
      <category>validation</category>
      <author>YEAHx4</author>
      <guid isPermaLink="true">https://yeahx4.tistory.com/9</guid>
      <comments>https://yeahx4.tistory.com/9#entry9comment</comments>
      <pubDate>Mon, 4 Dec 2023 15:46:14 +0900</pubDate>
    </item>
    <item>
      <title>Spring boot 초기 세팅 및 DB 연결</title>
      <link>https://yeahx4.tistory.com/8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;portpolent의 백엔드를 위해 Spring Boot와 DB를 설정하고 연결해 HTTP 요청을 잘 처리하는지 확인한다. IntelliJ Ultimate와 MariaDB를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script&gt;  if (window.location.pathname.split(&quot;/&quot;)[1] === &quot;m&quot; &amp;&amp; navigator.userAgent.indexOf(&quot;Tistory&quot;) === -1 &amp;&amp; navigator.userAgent.indexOf(&quot;Android&quot;) === -1) {    window.location.href = window.location.origin + window.location.pathname.substr(2);  }&lt;/script&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Spring Boot 프로젝트 시작하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인텔리제이에 spring boot프로젝트를 바로 만들 수 있는 기능이 있어서 그걸 사용했지만 &lt;a href=&quot;https://start.spring.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://start.spring.io/&lt;/a&gt; 에서 생성해도 동일하다. 자바는 17 버전, Gradle은 Kotlin, 프로젝트 언어는 자바, 스프링 부트 3.2 버전을 사용한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;366&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kY4x3/btsBkjbmynR/oOl7nlQHtKBq69kodIeOt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kY4x3/btsBkjbmynR/oOl7nlQHtKBq69kodIeOt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kY4x3/btsBkjbmynR/oOl7nlQHtKBq69kodIeOt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkY4x3%2FbtsBkjbmynR%2FoOl7nlQHtKBq69kodIeOt1%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;1372&quot; height=&quot;366&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;366&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;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Web&lt;/li&gt;
&lt;li&gt;MariaDB Driver&lt;/li&gt;
&lt;li&gt;JPA&lt;/li&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Lombok&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1476&quot; data-origin-height=&quot;1120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpAL5I/btsBgJJzqcQ/DvQo9prldVTCHbZ3WrApOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpAL5I/btsBgJJzqcQ/DvQo9prldVTCHbZ3WrApOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpAL5I/btsBgJJzqcQ/DvQo9prldVTCHbZ3WrApOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpAL5I%2FbtsBgJJzqcQ%2FDvQo9prldVTCHbZ3WrApOK%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;1476&quot; height=&quot;1120&quot; data-origin-width=&quot;1476&quot; data-origin-height=&quot;1120&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DB 연결하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA를 추가하고 스프링 프로젝트를 실행하면 오류와 함께 바로 종료된다. DB와 연결에 실패했기 때문이다. application.properties에 아래 내용을 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1701602149096&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://localhost:3306/portpolent
spring.jpa.open-in-view=false
spring.datasource.username=portpolent
spring.datasource.password=portpolent-db&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;spring.jpa.show-sql : 쿼리되는 SQL을 볼 수 있다. logger에 찍히진 않는다.&lt;/li&gt;
&lt;li&gt;spring.jpa.hibernate.ddl-auto : Entity가 변경되었을 때 어떤 작업을 수행할지 설정한다. update는 자동으로 변경된 내용에 맞춰 갱신한다. 다만, 실제 배포용 빌드에서는 다른 옵션을 사용하는 것이 좋다.&lt;/li&gt;
&lt;li&gt;spring.datasource.url : 데이터베이스의 URL이다. portpolent라는 이름으로 데이터베이스를 미리 생성해 놔야 한다.&lt;/li&gt;
&lt;li&gt;spring.datasource.username, password : DB에 접근하는 username과 password이다. 데이터베이스에 읽기, 쓰기 권한이 있어야 한다. 당연히 실제 빌드에서는 다른 username과 비밀번호를 사용하는 것이 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 참고 MariaDB에서 유저를 생성하는 법&lt;/p&gt;
&lt;pre id=&quot;code_1701602602879&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE USER 'portpolent'@'localhost' IDENTIFIED BY 'portpolent-db';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;localhost에서만 접근할 수 있는 portpolent라는 유저를 만들어 비밀번호를 portpolent-db로 설정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1701602675490&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GRANT ALL PRIVILEGES ON portpolent.* TO 'portpolent'@'localhost';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;portpolent 데이터베이스의 모든 권한을 portpolent유저에게 부여한다.&lt;/p&gt;
&lt;pre id=&quot;code_1701602709873&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FLUSH PRIVILEGES&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경된 권한을 반영한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 설정까지 완료하면 이제야 프로젝트를 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HelloController 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 HTTP 요청을 받을 수 있는지 테스트하기 위해 간단한 컨트롤러를 하나 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1701603048237&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package io.yeahx4.portpolent.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(&quot;/hello&quot;)
public class HelloController {
    @GetMapping(&quot;/&quot;)
    public String hello() {
        return &quot;Hello World&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 GetMapping에 /를 했기 때문에 /hello에 요청을 보내면 404 Not Found가 나온다. /hello/에 보내야 정상적인 응답을 얻을 수 있다. GET 요청을 보내면 Hello World를 기대했지만 그렇지 않다. Spring Security를 같이 추가했기 때문에 CORS에 의해 차단된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CORS 해결하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Security를 추가하면 기본적으로 여러 보안이 제공된다. 그중에는 REST API에는 필요 없는 것도 있고 수정할 필요가 있는 것도 있다. 옛날 버전의 Spring Boot에서는 WebMvcConfigurer 인터페이스를 구현해서 오버라이딩하는 방식으로 설정했지만 Bean을 사용하는 방향으로 변경되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1701603549634&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableWebSecurity
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .httpBasic(it -&amp;gt; it.disable())
                .csrf(it -&amp;gt; it.disable())
                .build();
    }

    @Bean
    public CorsConfigurationSource corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();

        List&amp;lt;String&amp;gt; origins = Arrays.asList(
                &quot;http://localhost&quot;,
                &quot;http://localhost:3000&quot;
        );

        config.setAllowCredentials(true);

        for (String origin : origins) {
            config.addAllowedOrigin(origin);
        }

        config.addAllowedHeader(&quot;*&quot;);
        config.addAllowedMethod(&quot;*&quot;);

        source.registerCorsConfiguration(&quot;/**&quot;, config);

        return source;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Security에서 기본으로 제공되는 httpBasic과 csrf 방어를 제거한다. UrlBasedCorsConfigurationSource를 통해 요청을 수락할 URL을 한정한다. OpenAPI가 아닌 이상 모든 origin에서 요청을 받는 것은 위험하다. CORS가 있어도 XSS 등의 취약점에는 위험할 수 있다.&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;모든 것을 설정하고 정말로 GET 요청을 날려 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;546&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bT6kZe/btsBmSqYrOO/Yky71wcMYfdpFsqgwu0oy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bT6kZe/btsBmSqYrOO/Yky71wcMYfdpFsqgwu0oy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bT6kZe/btsBmSqYrOO/Yky71wcMYfdpFsqgwu0oy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbT6kZe%2FbtsBmSqYrOO%2FYky71wcMYfdpFsqgwu0oy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;746&quot; height=&quot;546&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;546&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/개발일지</category>
      <author>YEAHx4</author>
      <guid isPermaLink="true">https://yeahx4.tistory.com/8</guid>
      <comments>https://yeahx4.tistory.com/8#entry8comment</comments>
      <pubDate>Sun, 3 Dec 2023 20:43:34 +0900</pubDate>
    </item>
  </channel>
</rss>