Table of Contents

Spring Framework (Java)

Getting Started

New Project

Basic File Structures

C:.                              
│  .gitignore		// gitignore 파일
│  build.gradle		// 빌드 스크립트
│  gradlew		// gradle 실행 sh 스크립트
│  gradlew.bat		// gradle 윈도우 실행 배치 파일
│  LICENSE		// 라이센스 정보
│  README.md		// 간단한 설명 파일
│  settings.gradle		// gradle 설정 파일,
│
├─.gradle ... 생략(자동 생성) ...
├─.idea ... 생략(자동 생성) ...
├─build ... 생략(자동 생성) ...
├─gradle		// gradle wrapper
│  └─wrapper
│          gradle-wrapper.jar
│          gradle-wrapper.properties
└─src
    ├─main		// 애플리케이션 메인
    │  ├─java		// 패키지 작성
    │  └─resources	// mapper xml, configuration yml, etc.
    │          application.yml	// 기본 애플리케이션 설정
    └─test		// 애플리케이션 테스트
        ├─java		// 테스트 작성
        └─resources	// 테스트용 resources

처리 영역 프레임워크 설명
Presentation Struts Struts 프레임워크는 UI Layer에 중점을 두고 개발된 MVC(Model View Controller) 프레임워크이다.
Spring Struts와 동일하게 MVC 아키텍처를 제공하는 UI Layer 프레임워크이다. 하지만 Struts 처럼 독립된 프레임워크는 아니고 Spring 프레임워크에 포함되어 있다.
Business Spring
(IoC, AOP)
Spring은 컨테이너 성격을 가지는 프레임워크이다. Springj의 IoC와 AOP 모듈을 이용하여 Spring 컨테이너에서 동작하는 엔터프라이즈 비즈니스 컴포넌트를 개발할 수 있다.
Persistence Hibernate or JPA Hibernate는 완벽한 ORM(Object Relation Mapping) 프레임워크이다. ORM 프레임워크는 SQL 명령어를 프레임워크가 자체적으로 생성하여 DB 연동을 처리한다. JPA는 Hibernate를 비롯한 모든 ORM의 공통 인터페이스를 제공하는 자바 표준 API이다.
Ibatis or Mybatis Ibatis 프레임워크는 개발자가 작성한 SQL 명령어와 자바 객체(VO 혹은 DTO)를 매핑해주는 기능을 제공하며, 기존에 사용하던 SQL 명령어를 재사용하여 개발하는 차세대 프로젝트에 유용하게 적용할 수 있다. Mybatis는 Ibatis에서 파생된 프로임워크로서 기본 개념과 문법은 거의 같다.

  ...
  <bean id="{id}" class="{class path}"/>
  ...

...
import org.springframework.context.support.AbstractAppicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

...
public class {class} 
{

  public static void main(String[] args) 
  {
    // 1. Spring 컨테이너 구동
    Abstract ApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
    
    // 2. Spring 컨테이너로부터 필요한 객체를 요청(Lookup).
    TV tv = (TV)factory.getBean("tv");
  }
}
...

구현 클래스 기능
GenericXmlApplicationContext 파일 시스템이나 클래스 경로에 있는 XML 설정 파일을 로딩하여 구동하는 컨테이너이다.
XmlWebApplicationContext 웹 기방의 스프링 애플리케이션을 개발할 때 사용하는 컨테이너이다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

<beans ...
  xmlns:p="http://www.springframework.org/schema/p"
  ...>

  <!-- 생성자 인젝션 -->
  <bean id="tv" class="polymorphism.SamsungTV">
    <constructor-arg ref="sony"></constructor-arg> <!-- 아래 bean의 id sony와 매칭 -->
    <constructor-arg value="2700000"></constructor-arg>
  </bean>
  
  <bean id="sony" class="polymorphism.SonySpeaker"></bean>
  <bean id="apple" class="polymorphism.AppleSpeaker"></bean>
  
  <!-- Setter Injection -->
  <bean id="tv" class="polymorphism.SamsungTV">
    <property name="speaker" ref="apple"></property>
    <property name="price" value="2700000"></property>
  </bean>
  
  <!-- p namespace -->
  <bean id ="tv" class="polymorphism.SamsungTV" p:speaker-ref="sony" p:price="2700000"/>

  <bean ...>
    <property name="addressList">
      <list>
        <value>something 1</value>
        <value>something 2</value>
      </list>
      
      <set value-type="java.lang.String">
        <value>something 1</value>
        <value>something 2</value>
        <value>something 2</value>
      <set>
      
      <map>
        <entry>
          <key><value>key value 1</value></key>
          <value>value 1</value>
        </entry>
        <entry>
          <key><value>key value 2</value></key>
          <value>value 2</value>
        </entry>
      </map>
      
      <props>
        <prop key="key1">value 1</prop>
        <prop key="key2">value 2</prop>
      </props>
    </property>

Annotation 기반 설정

어노테이션 설명
@Autowired 주로 변수 위에 설정하여 해당 타입의 객체를 찾아서 자동으로 할당한다.
org.springframework.beans.factory.annotation.Autowired
@Qualifier 특정 객체의 이름을 이용하여 의존성 주입할 때 사용한다.
org.springframework.beans.factory.annotation.Qualifier
@Inject @Autowired와 동일한 기능을 제공한다.
javax.annotation.Resource
@Resource @Autowired와 @qualifier의 기능을 결합한 어노테이션이다.
javax.inject.Inject
어노테이션 위치 의미
@Service XXXXServiceImpl 비즈니스 로직을 처리하는 Service 클래스
@Repository XXXDAO 데이터베이스 연동을 처리하는 DAO 클래스
@Controller XXXController 사용자 요청을 제어하는 Controller 클래스

Spring AOP

...
  <aop:config>
    <aop:pointcut id="allPointCut" expression="execution(* com.reth.biz..*Impl.*(..))"/>
    <aop:aspect ref="{bean id}">
      <aop:before pointcut-ref="allPointCut" method="{method}"/>
    </aop:aspect>
  </aop:config>
...

리턴타입 지정
표현식 설명
* 모든 리턴타입 허용
void 리턴타입이 void인 메소드 선택
!void 리턴타입이 void가 아닌 메소드 선택
패키지 지정
표현식 설명
com.reth.biz 정확하게 com.reth.biz 패키지만 선택
com.reth.biz.. com.reth.biz 패키지로 시작하는 모든 패키지 선택
com.reth..impl com.reth 패키지로 시작하면서 마지막 패키지 이름이 impl로 끝나는 패키지 선택
클래스 지정
표현식 설명
BoardServiceImpl 정확하게 BoardServiceImpl 클래스만 선택
*Impl 클래스 이름이 Impl로 끝나는 클래스만 선택
BoardService+ 클래스 이름 뒤에 '+'가 붙으면 해당 클래스로부터 파생된 모든 자식 클래스 선택, 인터페이스 뒤에 '+'가 붙으면 해당 인터페이스를 구현한 모든 클래스 선택
메소드 지정
표현식 설명
*(..) 가장 기본 설정으로 모든 메소드 선택
get*(..) 메소드 이름이 get으로 시작하는 모든 메소드 선택
매개변수 지정
표현식 설명
(..) 가장 기본 설정으로서 '..'은 매개변수의 개수와 타입에 제약이 없음을 의미
(*) 반드시 1개의 매개변수를 가지는 메소드만 선택
(com.reth.user.UserVO) 매개변수로 UserVO를 가지는 메소드만 선택. 이때 클래스의 패키지 경로가 반드시 포함되어야 함
(!com.reth.user.userVO) 매개변수로 UserVO를 가지지 않는 메소드만 선택
(Integer, ..) 한 개 이상의 매개변수를 가지되, 천 번째 매개변수의 타입이 Integer인 메소드만 선택
(Interger, *) 반드시 두 개의 매개변수를 가지되, 첫 번째 매개변수의 타입이 Integer인 메소드만 선택
동작 시점 설명
Before 비즈니스 메소드 실행 전 동작
After - After Returning: 비즈니스 메소드가 성공적으로 리턴되면 동작
- After Throwing: 비즈니스 메소드 실행 중 예외가 발생하면 동작(마치 try-catch 블록에서 catch 블록에 해당)
- After: 비즈니스 메소드가 실행된 수, 무조건 실행(try~catch~finally 블록에서 finally 블록에 해당)
Around Around는 메소드 호출 자체를 가로채 비즈니스 실행 전후에 처리할 로직을 삽입할 수 있음
메소드 설명
Signature getSignature() 클라이언트가 호출한 메소드의 시그니처(리턴타입, 이름, 매개변수) 정보가 저장된 Signature 객체 리턴
Object getTarget() 클라이언트가 호출한 비즈니스 메소드를 포함하는 비즈니스 객체 리턴
Object[] getArgs() 클라이언트가 메소드를 호출할 때 넘겨준 인자 목록을 Object 배열로 리턴
메소드명 설명
String getName() 클라이언트가 호출한 메소드 이름 리턴
String toLongString() 클라이언트가 호출한 메소드의 리턴타입, 이름, 매개변수를 패키지 경로까지 포함하여 리턴
String toShortString() 클라이언트가 호출한 메소드 시그니처를 축약한 문자열로 리턴

어노테이션 기반 AOP

Annotation 설정 @Service
public class LogAdvice{}
XML 설정 <bean id=“log” class=“com.reth.biz.common.LogAdvice”></bean>
어노테이션 설명
@Before 비즈니스 메소드 실행 전에 동작
@AfterReturning 비즈니스 메소드가 성공적으로 리턴되면 동작
@AfterThrowing 비즈니스 메소드 실행 중 예외가 발생하면 동작(마치 try~catch 블록에서 catch 블록에 해당)
@After 비즈니스 메소드가 실행된 후, 무조건 실행(try~catch~finally 블록에서 finally 블록에 해당)
@Around 호출 자체를 가로채 비즈니스 메소드 실행 전후에 처리할 로직을 삽입할 수 있음

package com.reth.biz.common;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class PointcutCommon
{
  @Pointcut("execution(* com.reth.biz..*Impl.*(..))")
  public void allPointcut() {}
  
  @Pointcut("execution(* com.reth.biz..*Impl.get*(..))")
  public void getPointcut() {}
}

...
  @Before("PointcutCommon.allPointcut()")
  public void beforeLog(JoinPoint jp)
  {
    ...
  }
...

Spring JDBC

스프링 JDBC 설정

...
  <!-- DBCP -->
  <dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>Commons-dbcp</artifactId>
    <version>1.4</version>
  </dependency>
...

...
  <!-- DataSource 설정 -->
  <bean id="dataSource" calss="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="org.h2.Driver" />
    <property name="url" value="jdbc:h2:tcp://localhost/~/test" />
    <property name="username" value="sa" />
    <property name="password" value="" />
  </bean>
...

jdbc.driver=org.h2.Driver
jdbc.url=jdbc:h2:tcp://localhost/~/test
jdbc.username=sa
jdbc.password=

...
  <!-- DataSource 설정 -->
  <context:property-placeholder location="classpath:config/database.properties" />
  
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driver}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
  </bean>
...

JdbcTemplate 메소드

package com.reth.biz.board.impl;

import java.sql.ResultSet;
import java.sql.SQLException;

import com.reth.biz.board.BoardVO;
import com.springframework.jdbc.core.RowMapper;

public class BoardRowMapper implements RowMapper<BoardVO>
{
  public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException
  {
    BoardVO board = new BoardVO();
    board.setSeq(rs.getInt("SEQ"));
    board.setTitle(rs.getString("TITLE"));
    board.setWriter(rs.getString("WRITER"));
    board.setContent(rs.getString("CONTENT"));
    board.setRegData(rs.getDate("REGDATE"));
    board.setCnt(rs.getInt("CNT"));
    return board;
  }
}

DAO 클래스 구현

...
@Repository
public class BoardDAOSpring extends JdbcDaoSupport
{
...
  @Autowired
  public void setSuperDataSource(DataSource dataSource)
  {
    super.setDataSource(dataSource);
  }
...
}

...

...
  <!-- Spring JDBC 설정 -->
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
  </bean>
...

...
@Repository
public class BoardDAOSpring
{
  @Autowired
  private JdbcTemplate jdbcTemplate;

  ...
}
...

...
@Service("boardService")
public class BoardServiceImpl implements BoardService
{
  @Autowired
  private BoardDAOSpring boardDAO;
  
  public void insertBoard(BoardVO vo)
  {
    boardDAO.insertBoard(vo);
  }
  
  ...
}
...

트랜잭션 처리

...
<!-- DataSource 설정 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
...
</bean>

<!-- Transaction 설정 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"></property>
</bean>
...

...
<!-- Transaction 설정 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"></property>
</bean>

<tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
    <tx:method name="get*" read-only="true" />
    <tx:method name="*" />
  </tx:attributes>
</tx:advice>
...

<tx:method> 속성
속성 의미
name 트랜잭션이 적용될 메소드 이름 지정
read-only 읽기 전용 여부 지정(기본값 false)
no-rollback-for 트랜잭션을 롤백하지 않을 예외 지정
rollback-for 트랜잭션을 롤백할 예외 지정

...
<tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
    <tx:method name="get*" read-only="true" />
    <tx:method name="*" />
  </tx:attributes>
</tx:advice>

<aop:config>
  <aop:pointcut id="txPointcut" expression="execution(* com.reth.biz..*(..))" />
  <aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice" />
</aop:config>
...

Model 1 아키텍처

Forward와 Redirect 차이

  • 포워드 방식은 RequestDispatcher를 이용하여 응답으로 사용할 JSP 화면으로 넘겨서, 포워드된 화면이 클라이언에 전송되는 방식. 한 번의 요청과 응답으로 처리. 실행속도는 빠르지만 클라이언트 브라우저에서 URL이 바뀌지 않아 응답이 어디에서 들어왔는지 확인 불가
  • 리다이렉트는 요청된 JSP에서 일단 브라우저로 응답 메시지를 보냈다가 다시 서버로 재요청하는 형식. 포워드 방식과 달리 일단 응답이 브라우저로 들어간 다음, 재요청하는 방식. 응답이 들어온 파일로 브라우저의 URL이 변경, 두 번의 요청과 응답으로 처리되므로 실행 속도는 포워드 방식보다 느림.

Model 2 아키텍처

MVC 프레임워크

클래스 기능
DispatcherServlet 유일한 서블릿 클래스로서 모든 클라이언트의 요청을 가장 먼저 처리하는 Front Controller
HandlerMapping 클라이언트의 요청을 처리할 Controller 매핑
Controller 실질적인 클라이언트의 요청 처리
ViewResolver Controller가 리턴한 View 이름으로 실행될 JSP 경로 완성

EL과 JSTL

  • EL(Expression Language); JSP 2.0에서 추가된 스크립트 언어, 기존의 표현식(Expression)을 대체하는 표현 언어.
    • <%= session.getAttribute(“userName”) %> → ${username} 과 같이 표현
  • JSTL(JSP Standard Tag Library); Scriptlet에서 if, for, switch 등과 같은 자바 코드를 사용해야 할 때, 자바 코드을을 태그 형태로 사용할 수 있도록 지원

Spring MVC 구조

  1. Client → (request) → DispatcherServlet
  2. DispatcherServlet → HandlerMapping
  3. DispatcherServlet → Controller
  4. DispatcherServlet ← ModelAndView ← Controller
  5. DispatcherServlet → ViewResolver
  6. DispatcherServlet → View
  1. 클라이언트로부터의 모든 “.do” 요청을 DispatcherServlet이 받는다
  2. DispatcherServlet은 HandlerMapping을 통해 요청을 치리할 Controller를 검색.
  3. DispatcherServlet은 검색된 Controller를 실행하여 클라이언트의 요청을 처리.
  4. Controller는 비즈니스 로직의 수행 결과로 얻어낸 Model 정보와 Model을 보여줄 View 정보를 ModelAndView 객체에 저장하여 리턴
  5. DispatcherServlet은 ModelAndView로부터 View 정보를 추출하고, ViewResolver를 이용하여 응답으로 사용할 View를 얻어냄.
  6. DispatcherServlet은 ViewResolver를 통해 찾아낸 View를 실행하여 응답 전송
  1. DispatcherServlet 등록 및 스프링 컨테이너 구동
    1. DispatcherServlet 등록; WEB-INF/web.xml
    2. 스프링 컨테이너 구동; DispatcherServlet.java
    3. 스프링 설정 파일 등록; 프로젝트 탐색창 WEB-INF 컨텍스트 메뉴 → [New] → [Other] → Spring 폴더에서 Spring Bean configuration file 선택 → [Next] → File name에 action-servlet.xml 파일 명입력 → [Finish] → 생성된 파일의 이름과 경로 확인
  2. 스프링 설정 파일 변경; WEB-INF 폴더 아래 config 폴더 생성 → 위에 생성한 파일 action-servlet.xml 파일 이동 → presentation-layer.xml로 이름 변경 → web.xml 파일 열고 DispatcherServlet 클래스 등록한 곳에 <init-param> 성정 추가

<servlet>
  <servlet-name>action</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/config/presentation-layer.xml</param-value>
  </init-param>
</servlet>

public class DispatcherServlet extends HttpServlet
{
  private String contextConfigiLocation;
  
  public void init(ServletConfig config) throws ServletException
  {
    contextConfigLocation = config.getInitParameter("contextConfigLocation");
    new XmlWebApplicationContext(contextConfigLocation);
  }
}

  1. 인코딩설정; web.xml 파일에 CharacterEncodingFilter 클래스를 필터로 등록

...
  <filter>
    <filter-name>characterEncoding</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>EUC-KR</param-value>
    </init-param>
  </filter>
  
  <filter-mapping>
    <filter-name>characterEncoding</filter-name>
    <url-pattern>*.do</url-pattern>
  </filter-mapping>
...

Spring MVC 적용

  1. Controller 구현; *Controller.java
  2. HandlerMapping 등록; presentation-layer.xml에 HandlerMapping과 Controller를 <bean> 등록

<?xml version="1.0" encoding="UTF-8" ?>
....
  <!-- HandlerMapping 등록 -->
  <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
      <props>
        <prop key="/login.do">login</prop>
      </props>
    </property>
  </bean>  
  
  <!-- Controller 등록 -->
  <bean id="login" class="com.reth.view.user.LoginController"></bean>
</beans>
....

....
  <!-- ViewResolver 등록 -->
  <bean id="viewResolver" class="org.springframework.web.servlet.viewInternalResourceResolver">
    <property name="prefix" value="/WEB-INF/board/"></property>
    <property name="suffix" value=".jsp"></property>
  </bean>
....

어노테이션 기반 MVC

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.2.xsd">
  <context:component-scan base-package="com.reth.view">
  </context:component-scan>
</beans>

@Controller

import org.springframework.stereotype.Controller;

@Controller
public class InsertBoardController
{
}

import 정리

만약 이미 다른 Controller가 import에 등록되었다면, 클래스 위에 @Controller를 설정할 때 자동완성이 다음처럼 이상하게 입력될 수 있다.

import org.springframework.web.servlet.mvc.Controller;
@org.springframework.stereotype.Controller
public class InsertBoardController implements Controller
{
  public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
  {
  }
}

이때는 import된 Controller를 제거하고 다시 @Controller를 자동완성하면 깔끔하게 처리된다. 또는 이클립스의 <Ctrl>+<Shift>+<O> 단축키를 눌러 import를 정리해도 된다.

@RequestMapping

....
import org.springframework.web.bind.annotation.RequestMapping;
....
  @RequestMapping(value="/insertBoard.do")
  public void insertBoard(HttpServletRequest request)
  {
  }
....

클라이언트 요청 처리

  1. HTTP요청; Start line, Message Header, Message Body;
    • 서블릿 컨테이너는 클라이언트의 HTTP 요청이 서버에 전달되는 순간
  2. HttpServletRequest
    • HttpServletRequest 객체를 생성하고 HTTP 프로토콜에 설정된 모든 정보를 추출하여 HttpServletRequest 객체에 저장
  3. DispatcherServlet의 service 메서드의 HttpServletRequest 인자
    • 그리고 이 HttpServletRequest 객체를 service() 메소드를 호출할 때, 인자로 전달
  1. 객체생성; 매개변수에 해당하는 VO 객체 생성
  2. 사용자 입력 값 전달; 사용자가 입력한 파라미터 값들을 추출하여 VO 객체에 저장. 이 때, VO 클래스의 setter 메소드들이 호출된다.
  3. 사용자가 요청한 액션에 해당 메소드를 호출할 때, 사용자 입력값들이 설정된 VO 객체가 인자로 전달된다.

리다이렉트로 넘기기

Controller 메소드가 실행되고 View 경로를 리턴하면 기본이 포워딩 방식이므로 url은 변경되지 않는다. 따라서 포워딩이 아닌 리다이렉팅을 원할 때는 “redirect:“라는 접두사를 붙여야 한다.

프레젠테이션 레이어와 비즈니스 레이어 통합

파일 업로드

  1. 파일 업로드 입력 화면
  2. Command 객체 수정; VO 클래스에 org.springframework.web.multipart.MultipartFile 타입의 변수 추가
  3. FileUpload 라이브러리 추가; pom.xml 에 dependency 추가
  4. MultipartResolver 설정; CommonsMultipartResolver 를 <bean> 등록
  5. 파일 업로드 처리

다국어 처리

스프링 부트

스프링 프레임워크 심화

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogEverything {
}

@LogEverything
public void placeOrder(int value) {

스프링 부트의 REST API

HTTP 요청 메소드
HTTP 요청 메소드 오퍼레이션
GET 읽기 - 리소스의 세부 정보 검색
POST 생성 - 새로운 아이템 또는 리소스 생성
PUT 업데이트/교체
PATCH 리소스 일부 업데이트/수정
DELETE 삭제
시나리오와 응답 상태 매핑
상태 응답 상태
요청 본문이 API 스펙을 충족하지 않는다. 세부사항이 충분하지 않거나 유효성 검사 오류가 있다. 400 BAD REQUEST
인증 또는 권한 부여에 실패했다. 401 UNAUTHORIZED
사용자는 제한을 초과하는 등 다양한 요인으로 인해 작업을 수행할 수 없다. 403 FORBIDDEN
리소스가 없다. 404 NOT FOUND
지원되지 않는 작업(예: GET만 허용되는 리소스에서 POST를 시도하는 경우) 405 METHOD NOT ALLOWED
서버에서 오류가 발생했다. 이상적으로는 이런 일이 발생하지 않아야 한다. 소비자는 문제를 해결할 수 없다. 500 INTERNAL SERVER ERROR

스프링 부트의 REST API 단위 테스트

스프링 시큐리티를 활용한 REST API

리액트 및 스프링 부트가 포함된 풀스택 앱

스프링 데이터

마이크로 서비스

스프링 부트 + 스프링 클라우드 => 마이크로서비스

리액티브 프로그래밍

스프링 모범 사례

스프링에서 코틀린 개발

References

Design Patterns used in Spring Framework

스프링 캐시