<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>bijou_xx_.log</title>
        <link>https://velog.io/</link>
        <description>Seize the day.🍀</description>
        <lastBuildDate>Wed, 03 May 2023 08:32:53 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>bijou_xx_.log</title>
            <url>https://velog.velcdn.com/images/bijou_xx_/profile/2edf1cda-dac5-4a61-9daa-41eba00a0bc4/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. bijou_xx_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bijou_xx_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[6. [스프링 입문] - AOP]]></title>
            <link>https://velog.io/@bijou_xx_/6.-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-AOP</link>
            <guid>https://velog.io/@bijou_xx_/6.-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-AOP</guid>
            <pubDate>Wed, 03 May 2023 08:32:53 GMT</pubDate>
            <description><![CDATA[<p><strong>AOP가 필요한 상황</strong></p>
<ul>
<li>모든 메소드의 호출 시간을 측정하고 싶다면?</li>
<li>공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern)</li>
<li>회원 가입 시간, 회원 조회 시간을 측정하고 싶다면?
<img src="https://velog.velcdn.com/images/bijou_xx_/post/08b0654d-630e-41ac-a700-a9e680e41cd9/image.png" alt=""></li>
<li><em>MemberService 회원 조회 시간 측정 추가*</em></li>
</ul>
<pre><code class="language-java">package hello.hellospring.service;
@Transactional
public class MemberService {
 /**
 * 회원가입
 */
 public Long join(Member member) {
 long start = System.currentTimeMillis();
 try {
 validateDuplicateMember(member); //중복 회원 검증
 memberRepository.save(member);
 return member.getId();
 } finally {
 long finish = System.currentTimeMillis();
 long timeMs = finish - start;
 System.out.println(&quot;join &quot; + timeMs + &quot;ms&quot;);
 }
 }
 /**
 * 전체 회원 조회
 */
 public List&lt;Member&gt; findMembers() {
 long start = System.currentTimeMillis();
 try {
 return memberRepository.findAll();
 } finally {
 long finish = System.currentTimeMillis();
 long timeMs = finish - start;
 System.out.println(&quot;findMembers &quot; + timeMs + &quot;ms&quot;);
 }
 }
}
</code></pre>
<h4 id="aop-적용---의존성-없음">AOP 적용 - 의존성 없음</h4>
<p>AOP: Aspect Oriented Programming</p>
<blockquote>
<p>공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern) 분리
<img src="https://velog.velcdn.com/images/bijou_xx_/post/77ff6399-ac75-4237-8741-089bad711376/image.png" alt=""></p>
</blockquote>
<p><strong>시간 측정 AOP 등록</strong></p>
<pre><code class="language-java">package hello.hellospring.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class TimeTraceAop {
 @Around(&quot;execution(* hello.hellospring..*(..))&quot;)
 public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
 long start = System.currentTimeMillis();
 System.out.println(&quot;START: &quot; + joinPoint.toString());
 try {
 return joinPoint.proceed();
 } finally {
 long finish = System.currentTimeMillis();
 long timeMs = finish - start;
 System.out.println(&quot;END: &quot; + joinPoint.toString()+ &quot; &quot; + timeMs +
&quot;ms&quot;);
 }
 }
}</code></pre>
<p><strong>해결</strong></p>
<ul>
<li>회원가입, 회원 조회등 핵심 관심사항과 시간을 측정하는 공통 관심 사항을 분리한다.</li>
<li>시간을 측정하는 로직을 별도의 공통 로직으로 만들었다.</li>
<li>핵심 관심 사항을 깔끔하게 유지할 수 있다.</li>
<li>변경이 필요하면 이 로직만 변경하면 된다.</li>
<li>원하는 적용 대상을 선택할 수 있다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[5. [스프링 입문] - 스프링 DB 접근 기술]]></title>
            <link>https://velog.io/@bijou_xx_/5.-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81-DB-%EC%A0%91%EA%B7%BC-%EA%B8%B0%EC%88%A0</link>
            <guid>https://velog.io/@bijou_xx_/5.-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81-DB-%EC%A0%91%EA%B7%BC-%EA%B8%B0%EC%88%A0</guid>
            <pubDate>Wed, 03 May 2023 07:53:16 GMT</pubDate>
            <description><![CDATA[<h3 id="순수-jdbc">순수 Jdbc</h3>
<p>환경 설정
<strong>build.gradle 파일에 jdbc, h2 데이터베이스 관련 라이브러리 추가</strong></p>
<pre><code>implementation &#39;org.springframework.boot:spring-boot-starter-jdbc&#39;
runtimeOnly &#39;com.h2database:h2&#39;</code></pre><p><strong>스프링 부트 데이터베이스 연결 설정 추가</strong>
<code>resources/application.properties</code></p>
<pre><code>spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
</code></pre><p><img src="https://velog.velcdn.com/images/bijou_xx_/post/3ab5f861-ce08-4f56-accc-2bac5843fc49/image.png" alt=""></p>
<blockquote>
<ul>
<li>개방-폐쇄 원칙(OCP, Open-Closed Principle)</li>
</ul>
</blockquote>
<ul>
<li>확장에는 열려있고, 수정, 변경에는 닫혀있다.<ul>
<li>스프링의 DI (Dependencies Injection)을 사용하면 기존 코드를 전혀 손대지 않고, 설정만으로 구현 클래스를 변경할 수 있다.</li>
<li>회원을 등록하고 DB에 결과가 잘 입력되는지 확인하자.</li>
<li>데이터를 DB에 저장하므로 스프링 서버를 다시 실행해도 데이터가 안전하게 저장된다.</li>
</ul>
</li>
</ul>
<h3 id="jpa">JPA</h3>
<p>🔺JPA는 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행해준다.</p>
<p>🔺JPA를 사용하면, SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환을 할 수 있다.</p>
<p>🔺JPA를 사용하면 개발 생산성을 크게 높일 수 있다.</p>
<h3 id="스프링-데이터-jpa">스프링 데이터 JPA</h3>
<blockquote>
<ul>
<li>스프링 부트와 JPA만 사용해도 개발 생산성이 많이 증가한다</li>
</ul>
</blockquote>
<ul>
<li>리포지토리에 구현 클래스 없이 인터페이스 만으로 개발을 완료할 수 있다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[4. [스프링 입문] - 스프링 빈과 의존관계]]></title>
            <link>https://velog.io/@bijou_xx_/4.-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84</link>
            <guid>https://velog.io/@bijou_xx_/4.-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88%EA%B3%BC-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84</guid>
            <pubDate>Wed, 03 May 2023 06:44:44 GMT</pubDate>
            <description><![CDATA[<span style="color:indianred">
- 컴포넌트 스캔과 자동 의존관계 설정 <br>
- 자바 코드로 직접 스프링 빈 등록하기
</span>

<h3 id="컴포넌트-스캔과-자동-의존관계-설정">컴포넌트 스캔과 자동 의존관계 설정</h3>
<p><strong>회원 컨트롤러에 의존관계 추가</strong></p>
<pre><code class="language-java">package hello.hellospring.controller;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class MemberController {
 private final MemberService memberService;
 @Autowired
 public MemberController(MemberService memberService) {
 this.memberService = memberService;
 }
}
</code></pre>
<blockquote>
<p>memberService가 스프링 빈으로 등록되어 있지 않다.</p>
</blockquote>
<p><strong>스프링 빈을 등록하는 2가지 방법</strong>
🔸컴포넌트 스캔과 자동 의존관계 설정
🔸자바 코드로 직접 스프링 빈 등록하기</p>
<p><strong>컴포넌트 스캔 원리</strong>
🔸<code>@Component</code> 애노테이션이 있으면 스프링 빈으로 자동 등록된다.
🔸<code>@Controller</code> 컨트롤러가 스프링 빈으로 자동 등록된 이유도 컴포넌트 스캔 때문이다.</p>
<p>🔸<code>@Component</code> 를 포함하는 다음 애노테이션도 스프링 빈으로 자동 등록된다.
  🔹<code>@Controller</code>
  🔹<code>@Service</code>
  🔹<code>@Repository</code></p>
<p><strong>회원 서비스 스프링 빈 등록</strong></p>
<pre><code class="language-java">@Repository
public class MemoryMemberRepository implements MemberRepository {</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[3. [스프링 입문] - 회원 관리 예제 - 백엔드 개발]]></title>
            <link>https://velog.io/@bijou_xx_/3.-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C</link>
            <guid>https://velog.io/@bijou_xx_/3.-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C</guid>
            <pubDate>Wed, 03 May 2023 06:44:39 GMT</pubDate>
            <description><![CDATA[<h3 id="비즈니스-요구사항-정리">비즈니스 요구사항 정리</h3>
<ul>
<li>데이터 : 회원ID,이름</li>
<li>기능 : 회원 등록, 조회</li>
<li>아직 데이터 저장소가 선정되지 않음(가상의 시나리오)</li>
</ul>
<h3 id="회원-도메인과-리포지토리-만들기">회원 도메인과 리포지토리 만들기</h3>
<p><strong>회원 객체</strong></p>
<pre><code class="language-java">package hello.hellospring.domain;
public class Member {

 private Long id;
 private String name;
 public Long getId() {
 return id;
 }
 public void setId(Long id) {
 this.id = id;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
}
회원 리포지토리 인터페이스
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import java.util.List;
import java.util.Optional;
public interface MemberRepository {
 Member save(Member member);
 Optional&lt;Member&gt; findById(Long id);
 Optional&lt;Member&gt; findByName(String name);
 List&lt;Member&gt; findAll();
}</code></pre>
<p><strong>회원 리포지토리 인터페이스</strong></p>
<pre><code class="language-java">package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import java.util.List;
import java.util.Optional;
public interface MemberRepository {
 Member save(Member member);
 Optional&lt;Member&gt; findById(Long id);
 Optional&lt;Member&gt; findByName(String name);
 List&lt;Member&gt; findAll();
}</code></pre>
<p><strong>회원 리포지토리 메모리 구현체</strong></p>
<pre><code class="language-java">package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import java.util.*;
/**
 * 동시성 문제가 고려되어 있지 않음, 실무에서는 ConcurrentHashMap, AtomicLong 사용 고려
 */
public class MemoryMemberRepository implements MemberRepository {
 private static Map&lt;Long, Member&gt; store = new HashMap&lt;&gt;();
 private static long sequence = 0L;
 @Override
 public Member save(Member member) {
 member.setId(++sequence);
 store.put(member.getId(), member);
 return member;
 }
 @Override
 public Optional&lt;Member&gt; findById(Long id) {
 return Optional.ofNullable(store.get(id));
 }
 @Override
 public List&lt;Member&gt; findAll() {
 return new ArrayList&lt;&gt;(store.values());
 }
 @Override
 public Optional&lt;Member&gt; findByName(String name) {
 return store.values().stream()
 .filter(member -&gt; member.getName().equals(name))
 .findAny();
 }
 public void clearStore() {
 store.clear();
 }
}</code></pre>
<h3 id="회원-리포지토리-태스트-케이스-작성">회원 리포지토리 태스트 케이스 작성</h3>
<p><strong>회원 리포지토리 메모리 구현체 테스트</strong>
<code>src/test/java</code> 하위 폴더에 생성한다</p>
<pre><code class="language-java">package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.*;
class MemoryMemberRepositoryTest {
 MemoryMemberRepository repository = new MemoryMemberRepository();
 @AfterEach
 public void afterEach() {
 repository.clearStore();
 }
 @Test
 public void save() {
 //given
 Member member = new Member();
 member.setName(&quot;spring&quot;);
 //when
 repository.save(member);
 //then
 Member result = repository.findById(member.getId()).get();
 assertThat(result).isEqualTo(member);
 }
 @Test
 public void findByName() {
 //given
 Member member1 = new Member();
 member1.setName(&quot;spring1&quot;);
 repository.save(member1);
 Member member2 = new Member();
 member2.setName(&quot;spring2&quot;);
 repository.save(member2);
 //when
 Member result = repository.findByName(&quot;spring1&quot;).get();
 //then
 assertThat(result).isEqualTo(member1);
 }
 @Test
 public void findAll() {
 //given
 Member member1 = new Member();
 member1.setName(&quot;spring1&quot;);
 repository.save(member1);
 Member member2 = new Member();
 member2.setName(&quot;spring2&quot;);
 repository.save(member2);
 //when
 List&lt;Member&gt; result = repository.findAll();
 //then
 assertThat(result.size()).isEqualTo(2);
 }
}</code></pre>
<h3 id="회원-서비스-개발">회원 서비스 개발</h3>
<pre><code class="language-java">package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import java.util.List;
import java.util.Optional;
public class MemberService {
 private final MemberRepository memberRepository = new
MemoryMemberRepository();
 /**
 * 회원가입
 */
 public Long join(Member member) {
 validateDuplicateMember(member); //중복 회원 검증
 memberRepository.save(member);
 return member.getId();
 }
 private void validateDuplicateMember(Member member) {
 memberRepository.findByName(member.getName())
 .ifPresent(m -&gt; {
 throw new IllegalStateException(&quot;이미 존재하는 회원입니다.&quot;);
 });
 }
 /**
 * 전체 회원 조회
 */
 public List&lt;Member&gt; findMembers() {
 return memberRepository.findAll();
 }
 public Optional&lt;Member&gt; findOne(Long memberId) {
 return memberRepository.findById(memberId);
 }
}
</code></pre>
<h3 id="회원-서비스-테스트">회원 서비스 테스트</h3>
<p><strong>기존에는 회원 서비스가 메모리 회원 리포지토리를 직접 생성하게 했다.</strong></p>
<pre><code class="language-java">public class MemberService {
 private final MemberRepository memberRepository = 
 new MemoryMemberRepository();
}</code></pre>
<p><strong>*회원 리포지토리의 코드가
회원 서비스 코드를 DI 가능하게 변경한다</strong></p>
<pre><code class="language-java">public class MemberService {
 private final MemberRepository memberRepository;
 public MemberService(MemberRepository memberRepository) {
 this.memberRepository = memberRepository;
 }
 ...
}</code></pre>
<p><strong>회원 서비스 테스트</strong></p>
<pre><code class="language-java">package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemoryMemberRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
class MemberServiceTest {
 MemberService memberService;
 MemoryMemberRepository memberRepository;
 @BeforeEach
 public void beforeEach() {
 memberRepository = new MemoryMemberRepository();
 memberService = new MemberService(memberRepository);
 }
 @AfterEach
 public void afterEach() {
 memberRepository.clearStore();
 }
 @Test
 public void 회원가입() throws Exception {
 //Given
 Member member = new Member();
 member.setName(&quot;hello&quot;);
 //When
 Long saveId = memberService.join(member);
 //Then
 Member findMember = memberRepository.findById(saveId).get();
 assertEquals(member.getName(), findMember.getName());
 }
 @Test
 public void 중복_회원_예외() throws Exception {
 //Given
 Member member1 = new Member();
 member1.setName(&quot;spring&quot;);
 Member member2 = new Member();
 member2.setName(&quot;spring&quot;);
 //When
 memberService.join(member1);
 IllegalStateException e = assertThrows(IllegalStateException.class,
 () -&gt; memberService.join(member2));//예외가 발생해야 한다.
 assertThat(e.getMessage()).isEqualTo(&quot;이미 존재하는 회원입니다.&quot;);
 }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[2. [스프링 입문] - 웹 개발 기초]]></title>
            <link>https://velog.io/@bijou_xx_/2.-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@bijou_xx_/2.-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Wed, 03 May 2023 06:44:32 GMT</pubDate>
            <description><![CDATA[<h3 id="스프링-웹-개발-기초">스프링 웹 개발 기초</h3>
<blockquote>
<p>🔸정적 컨텐츠
🔸MVC와 템플릿 엔진
🔸API</p>
</blockquote>
<h3 id="정적-컨텐츠static">정적 컨텐츠(static)</h3>
<p>모든 사용자의 웹페이지에서 콘텐츠가 동일하게 유지</p>
<ul>
<li>실행 방법 : <code>localhost:8080/[파일이름]</code></li>
</ul>
<p><code>resources/static/hello-static.html</code></p>
<pre><code>&lt;!DOCTYPE HTML&gt;
&lt;html&gt;
&lt;head&gt;
 &lt;title&gt;static content&lt;/title&gt;
 &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
정적 컨텐츠 입니다.
&lt;/body&gt;
&lt;/html&gt;</code></pre><p><strong>정적 컨텐츠 이미지</strong> 
<img src="https://velog.velcdn.com/images/bijou_xx_/post/8b510104-dd83-4ace-989d-e7cbe2f63d61/image.png" alt=""></p>
<blockquote>
<p>1) 웹브라우저에서 요청이 들어오면 스프링부트의 내장 톰캣 서버가 요청을 받는다.
2) 요청을 스프링에게 넘기고 스프링 컨테이너에서 해당 리소스와 매핑이 되는 컨트롤러를 찾는다.
3) 만약 없다면 /resources/static 에서 해당 html 파일을 찾아서 웹브라우저에 전송해준다.</p>
</blockquote>
<h3 id="mvc와-템플릿-엔진">MVC와 템플릿 엔진</h3>
<ul>
<li>MVC : Model,View,Controller</li>
<li>실행 방법 : <code>localhost:8080/[파일이름]?변수명=값</code></li>
</ul>
<p><strong>Controller</strong></p>
<pre><code class="language-java">@Controller
public class HelloController {
 @GetMapping(&quot;hello-mvc&quot;)
 public String helloMvc(@RequestParam(&quot;name&quot;) String name, Model model) {
 model.addAttribute(&quot;name&quot;, name);
 return &quot;hello-template&quot;;
 }
}</code></pre>
<p><strong>View</strong>
<code>resources/templates/hello-template.html</code></p>
<pre><code>&lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
&lt;body&gt;
&lt;p th:text=&quot;&#39;hello &#39; + ${name}&quot;&gt;hello! empty&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="mvc템플릿-엔진-이미지">MVC,템플릿 엔진 이미지 <img src="https://velog.velcdn.com/images/bijou_xx_/post/17387302-9265-4990-acb7-e3ec474951ed/image.jpg" alt=""></h4>
<blockquote>
<p>1) 요청이 오면 내장 톰캣 서버가 스프링에게 요청을 넘긴다.
2) 스프링은 스프링 컨테이너에서 요청과 관련된 컨트롤러를 찾고 메서드를 실행한다.
3) 메서드는 입력받은 파라미터를 모델에 저장하고 viewName을 리턴한다.
4) viewResolver가 viewName과 맞는 html 파일을 찾아주고 모델을 토대로 html을 렌더링하여 브라우저에 전달한다.</p>
</blockquote>
<ul>
<li>/hello-mvc 파일로 접근하면 오류 발생</li>
<li>웹브라우저에서 get방식으로 파라미터를 넘길때 <code>?[변수명]=&#39;값</code> 을 추가해주면 파라미터가 같이 전달된다. </li>
</ul>
<h3 id="api">API</h3>
<p><strong>@ResponseBody 문자 반환</strong></p>
<blockquote>
<p>http의 body에 문자 내용을 직접 반환</p>
</blockquote>
<pre><code class="language-java">@Controller
public class HelloController {
 @GetMapping(&quot;hello-string&quot;)
 @ResponseBody
 public String helloString(@RequestParam(&quot;name&quot;) String name) {
 return &quot;hello &quot; + name;
 }
}</code></pre>
<ul>
<li><code>@ResponseBody</code>를 사용하면 뷰 리졸버(<code>viewResolver</code>)를 사용하지 않음</li>
</ul>
<p><strong>@ResponseBody 객체 반환</strong>
🔹<code>@ResponseBody</code> 를 사용하고, 객체를 반환하면 객체가 JSON으로 변환됨
🔹JSON이란 키(key)와 값(value)로 이루어진 자료구조</p>
<pre><code class="language-java">@Controller
public class HelloController {
 @GetMapping(&quot;hello-api&quot;)
 @ResponseBody
 public Hello helloApi(@RequestParam(&quot;name&quot;) String name) {
 Hello hello = new Hello();
 hello.setName(name);
 return hello;
 }
 static class Hello {
 private String name;
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 }
}</code></pre>
<p>*<em>@ResponseBody 사용 원리 *</em> 
<img src="https://velog.velcdn.com/images/bijou_xx_/post/d6672082-2083-4f32-a0cd-a57908063f32/image.jpg" alt=""></p>
<blockquote>
<p>1) 요청이 오면 톰캣은 요청을 스프링에 전달한다.
2) 스프링은 요청과 관련된 컨트롤러를 찾는다.
3) @ResponseBody 어노테이션을 만나면 메서드의 리턴값을 HttpMessageConverter로 넘긴다.
4) 객체를 Json 형식으로 바꾸고 요청한 웹브라우저나 서버에 전달한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[1. [스프링 입문] - 프로젝트 환경설정 ]]></title>
            <link>https://velog.io/@bijou_xx_/1.-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@bijou_xx_/1.-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95</guid>
            <pubDate>Wed, 03 May 2023 06:44:14 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bijou_xx_/post/f651803a-ff6c-4af6-ad95-bf269f093edb/image.png" alt=""></p>
<h3 id="프로젝트-환경설정">프로젝트 환경설정</h3>
<span style="color:red">
- 프로젝트 생성 <br>
- 라이브러리 살펴보기<br>
- View 환경설정<br>
- 빌드하고 실행하기<br>
</span>

<h3 id="프로젝트-생성">프로젝트 생성</h3>
<blockquote>
<h4 id="사전준비물">사전준비물</h4>
<p>🔸 java 17 설치
🔸 IDE : Intellij 또는 Eclipse 설치</p>
</blockquote>
<blockquote>
<h3 id="프로젝트-선택">프로젝트 선택</h3>
<p>Project: Gradle - Groovy Project
Spring Boot: 3.0.6
Language: Java
Packaging: Jar
Java: 17
Project Metadata
groupId: hello
artifactId: hello-spring
Dependencies: Spring Web, Thymeleaf</p>
</blockquote>
<blockquote>
<h3 id="동작-확인">동작 확인</h3>
</blockquote>
<ul>
<li>기본 메인 클래스 실행</li>
<li>스프링 부트 메인 실행 후 에러페이지로 간단하게 동작 확인( <code>http://localhost:8080</code> )</li>
</ul>
<h3 id="라이브러리-살펴보기">라이브러리 살펴보기</h3>
<blockquote>
<p>Gradle은 의존관계가 있는 라이브러리를 함께 다운로드</p>
</blockquote>
<p>🔷 스프링 부트 라이브러리 </p>
<ul>
<li><p>spring-boot-starter-web   </p>
<ul>
<li>spring-boot-starter-tomcat: 톰캣 (웹서버)</li>
<li>spring-webmvc: 스프링 웹 MVC</li>
</ul>
</li>
<li><p>spring-boot-starter-thymeleaf: 타임리프 템플릿 엔진(View)</p>
</li>
<li><p>spring-boot-starter(공통): 스프링 부트 + 스프링 코어 + 로깅</p>
<ul>
<li>spring-boot<ul>
<li>spring-core</li>
</ul>
</li>
<li>spring-boot-starter-logging<ul>
<li>logback, slf4j</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>🔷 테스트 라이브러리</p>
<ul>
<li>spring-boot-starter-test<ul>
<li>junit: 테스트 프레임워크</li>
<li>mockito: 목 라이브러리</li>
<li>assertj: 테스트 코드를 좀 더 편하게 작성하게 도와주는 라이브러리</li>
<li>spring-test: 스프링 통합 테스트 지원</li>
</ul>
</li>
</ul>
<h3 id="view-환경설정">View 환경설정</h3>
<h4 id="welcome-page-만들기">Welcome Page 만들기</h4>
<p>  <code>resources/static/index.html</code></p>
<pre><code>&lt;!DOCTYPE HTML&gt;
&lt;html&gt;
&lt;head&gt;
 &lt;title&gt;Hello&lt;/title&gt;
 &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
Hello
&lt;a href=&quot;/hello&quot;&gt;hello&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>localhost:8080을 들어갔을 때 처음 보여지는 페이지를 <code>index.html</code>로 보여준다</p>
<h3 id="thymeleaf-템플릿-엔진">thymeleaf 템플릿 엔진</h3>
<pre><code class="language-java">@Controller
public class HelloController {
 @GetMapping(&quot;hello&quot;)
 public String hello(Model model) {
 model.addAttribute(&quot;data&quot;, &quot;hello!!&quot;);
 return &quot;hello&quot;;
 }
}</code></pre>
<p><code>resources/templates/hello.html</code></p>
<pre><code>&lt;!DOCTYPE HTML&gt;
&lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
&lt;head&gt;
 &lt;title&gt;Hello&lt;/title&gt;
 &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p th:text=&quot;&#39;안녕하세요. &#39; + ${data}&quot; &gt;안녕하세요. 손님&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="동작-환경-그림">동작 환경 그림 <img src="https://velog.velcdn.com/images/bijou_xx_/post/1dd1ed66-aa1d-4438-9627-68d8970e6603/image.jpg" alt=""></h4>
<blockquote>
<p>1) 웹 브라우저에서 localhost:8080/hello을 요청한다
2) 내장 톰켓 서버를 통해 스프링 컨테이너에서 helloController에서 리턴 값으로 문자를 반환한다 
3) 뷰 리졸버가 변환한 값을 화면을 찾아서 처리한다</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>