스프링 부트 기초

 

설치

spring.io 접속

projects > spring initializr > 설정 후 Generate 를 눌러 다운로드




아래 경로의 파일을 열고 실행해 본다.

다음 주소를 입력한다. > http://localhost:8080/


프론트엔드 vs 백엔드

Java는 Node.js 를 쓰기 위해 사용하는 게 일반적임.


REST API 란?

  • 클라이언트와 서버가 데이터를 주고 받는 약속된 방식
  • HTTP 요청방식: 
    • GET(받기), POST(보내기), PUT(수정하기), DELETE(삭제하기)
  • JSON 데이터를 주고받음


Main 파일

main > java 경로 바로 아래에 프로젝트명으로 생성한 파일

SpringRestApplication.java


어노테이션

  • Controller - 요청을 받아 처리
  • Service - 계산 및 조건 처리
  • Repostitory - 데이터 연결


Bean 빈

  • 빈(Bean) = 스프링 프레임워크에서 스프링 컨테이너가 관리하는 자바 객체.
  • 컨테이너 = 빈이 만들어지고 관리되는 상자
  • 개발자가 직접 new로 만들지 않고, 스프링이 대신 생성·관리.
  • 어노테이션이나 설정 파일로 등록.
  • 객체 생성, 의존성 주입, 생명주기 관리 등 다양한 역할을 담당.


@SpringBootApplication

  • 설정 클래스 등록: 해당 클래스를 스프링 설정 파일로 인식해서, @Bean 메서드로 직접 빈을 등록할 수 있습니다.
  • 자동 설정: Spring Boot가 프로젝트에 추가된 라이브러리(JAR)와 설정을 바탕으로 필요한 환경설정을 자동으로 해줍니다. 예를 들어, spring-boot-starter-web이 있으면 Spring MVC가 자동 구성됩니다.
  • 컴포넌트 스캔: 메인 클래스가 위치한 패키지와 그 하위 패키지에서 @Component, @Service, @Repository, @Controller가 붙은 클래스를 자동으로 스프링 빈으로 등록합니다


@Controller - 가장 먼저 작동되는 부분

@Controller
public class ExampleController {
    @GetMapping("/hello")
    @ResponseBody
    public String hello() {
        return "안녕하세요!";
    }
}

// RestController = Controller + ResponseBody
@RestController
public class HelloController {
    private final HelloService helloService;
    public HelloController(HelloService helloService) {
        this.helloService = helloService;
    }
    @GetMapping("/hello")
    public String sayHello() {
        return helloService.getHello();
    }
}

@GetMapping

get 방식으로 넘어온 주소가 맞으면 작동

@PostMapping

post 방식으로 넘어온 주소가 맞으면 작동

return의 차이


@Autowired

private JoinService joinService;
아래 내용을 자동으로 지원해 준다.

private HelloService helloService;
    // 가져온 메서드를 해당 인스턴스에 전달처리
    public HelloController(HelloService helloService){
        this.helloService = helloService;
    }


@Service

@Service
public class HelloService {
    public String getHello() {
        return "Hello from Service!";
    }
}

@Repository

@Repository
public interface HelloRepository extends JpaRepository<User, Long> {
    // 기본 CRUD 메서드는 자동 구현됨
}


작동방식 정리

1. controller - 각 페이지이별 컨트롤러 파일 확인
2. 각 컨트롤러 파일안쪽에서 GetMapping, PostMapping으로 어떤 URL요청이 들어오는지 확인
3. 각 URL 요청에 따라 실행되는 메서드가 어떤 문자를 반환하는지 확인
4. resources폴더 안쪽에서 매칭 파일명의 html이 어떤 UI 렌더링 하는지 확인
5. layout.html과 각 페이지별.html이 어떤식으로 연결되어 있는지 확인

실습

HelloController.java

HelloService 클래스는 내부 코드를 숨기기 위해, 객체를 생성해서 private로 담아 사용한다. 
package com.example.spring_rest.controller;

import com.example.spring_rest.service.HelloService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

//rest api를 간단히 구현하게 해주는 어노테이션
@RestController
public class HelloController {
// HelloService 클래스 서비스 패키지에서 private으로 가져옴 (보안이슈)
private HelloService helloService;

// 가져온 메서드를 해당 인스턴스에 전달처리
public HelloController(HelloService helloService){
this.helloService = helloService;
}

// "/hello" url 접속시 sayHello함수 호출
// sayHello함수 안쪽에는 은닉화 해놓은 서비스 계층 함수 실행
// controller(요청) + service(실행 로직)을 분리해서 관리
@GetMapping("/hello")
public String sayHello(){
return helloService.getHello();
}
}


HelloService.java

package com.example.spring_rest.service;

import org.springframework.stereotype.Service;

@Service
public class HelloService {

public String getHello(){
return "Hello from Service";
}
}


빌드 설정 파일에 thymeleaf 의존성 주입

src / build.gradle

dependencies항목 제일 아래쪽에 tymeleaf를 의존성 추가

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.1.0'
}


공통의 레이아웃 템플릿 생성

src / main /resource / template / layout.html 파일 추가 (공통의 템플릿 레이아웃 추가)

layout:fragment="a" : a를 제외한 모두를 점령한다. fragment = 'a' 부분만 남겨 놓는다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <!--공통 헤더 영역-->
  <header>
    <h1><a href="/">Logo</a></h1>
    <nav>
      <a href="/join">Join Member</a>
    </nav>
  </header>
  <!--  각 페이지 내용이 삽입될 위치   -->
  <main  layout:fragment="body"> </main>
  <!--공통 푸터 영역-->
  <footer>
    2025 alpaco &copy; All rights reserved.
  </footer>
</body>
</html>


메인 페이지에 레이아웃 템플릿 연결

src / main /resources / template / index.html

layout:fragment="a" : a의 layout을 그대로 사용하되, 남겨진 해당 부분만 사용할 수 있다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout}">
<head>
    <meta charset="UTF-8">
    <title>Join</title>
</head>
<body>
    <section  layout:fragment="body">
        <h1>Main Page</h1>
    </section>
</body>
</html>


html로 값을 java로 전달받아 다시 html로 출력

JoinController

/join 에서 작동

package com.example.basic.controller;

import com.example.basic.service.JoinService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
//Model: 컨트롤러에서 템플릿에 데이터를 전달시 필요한 스프링 내장 객체
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Controller
public class JoinController {

@Autowired
private JoinService joinService;

@GetMapping("/join")
public String join() {
return "join";
}

@PostMapping("/join/create")
public String create(
@RequestParam("uname") String uname,
@RequestParam("email") String email,
@RequestParam("colors") String colors,
Model model // Model 타입의 파라미터 추가
) {
// 서비스 메서드로 부터 전달받은 문자열을 전달 받음
String msg = joinService.processJoin(uname, email, colors);
//추가한 파라미터로 자동 전달된 인스턴스의 addAttribute라는 메서드를 통해
// {"data":전달할 문자열} 형식의 데이터를 index 템플릿에 전달
model.addAttribute("data", msg);
return "index";
}
}


join.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout}">
<head>
<meta charset="UTF-8">
<title>Join</title>
</head>
<body>
<section layout:fragment="body">
<h1>Join Member</h1>

<form action="/join/create" method="post">
<h2><label for="uname">User Name</label></h2>
<input type="text" name="uname" id="uname" placeholder="사용자 이름을 입력하세요.">

<h2><label for="email">Email</label></h2>
<input type="email" name="email" id="email" placeholder="이메일 주소를 입력하세요.">

<h2>Favorait Color</h2>
<div>
<label for="red">red</label>
<input type="radio" name="colors" id="red" value="red" checked>

<label for="green">green</label>
<input type="radio" name="colors" id="green" value="green">

<label for="blue">red</label>
<input type="radio" name="colors" id="blue" value="blue">
</div>

<div>
<input type="reset" value="cancel">
<input type="submit" value="send">
</div>
</form>
</section>
</body>
</html>


JoinService.java

package com.example.basic.service;

import org.springframework.stereotype.Service;

@Service
public class JoinService {
public String processJoin(String uname, String email, String colors) {
// 컨트롤러를 통해 실제 템플릿에 전달된 데이터가 매핑된 태그 문자열 반환
String result = "name: " + uname + "<br/>"
+ "email: " + email + "<br/>"
+ "my color: " + colors;
return result;
}
}


index.html

/join/create 에서 작동'

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout}">
<head>
<meta charset="UTF-8">
<title>Main</title>
</head>
<body>
<section layout:fragment="body">
<h1>Main Page</h1>
<div th:utext="${data}"></div>
</section>
</body>
</html>



낱개 파리미터 전달 방식이 아닌 객체 전달 방식

As-is: @RequestParam("uname") String uname,
To-be: @ModelAttribute JoinDTO formDTO


JoinDTO 객체 생성
package com.example.basic.dto;

public class JoinDTO {
private String uname;
private String email;
private String colors;

// 인스턴스 복사하는 성성자 함수
public JoinDTO(){}

// 각 폼의 항목별로 getter, setter 등록
public String getUname(){
return uname;
}
public void setUname(String uname){
this.uname = uname;
}

public String getEmail(){
return email;
}
public void setEmail(String email){
this.email = email;
}

public String getColors(){
return colors;
}
public void setColors(String colors){
this.colors = colors;
}
}


JoinController.java

@PostMapping("/join/create")
public String create(
// 매개변수 용도의 객체
@ModelAttribute JoinDTO formDTO,
// return에 사용하는 라이브러리 객체
Model model
) {
// 서비스 메서드로 부터 전달받은 문자열을 전달 받음
String msg = joinService.processJoin(formDTO);
model.addAttribute("data", msg);
return "index";
}

  • model을 선언할 때, 
    • {joinDTO=com.example.basic.dto.JoinDTO@18ca2bfc, org.springframework.validation.BindingResult.joinDTO=org.springframework.validation.BeanPropertyBindingResult: 0 errors}

  • model.addAttribute 를 사용할 때,
    • {joinDTO=com.example.basic.dto.JoinDTO@18ca2bfc, org.springframework.validation.BindingResult.joinDTO=org.springframework.validation.BeanPropertyBindingResult: 0 errors, data=</br><ul><li>name: 남상도</li><li>email: sangdo@hanmail.net</li><li>my color: green</li></ul></br>}


JoinService.java

public class JoinService {
public String processJoin(JoinDTO dto) {
// 컨트롤러를 통해 실제 템플릿에 전달된 데이터가 매핑된 태그 문자열 반환
String result = "</br><ul><li>name: " + dto.getUname() + "</li>"
+ "<li>email: " + dto.getEmail() + "</li>"
+ "<li>my color: " + dto.getColors() + "</li></ul></br>";
return result;
}
}












댓글 쓰기

다음 이전