06_Spring Boot 프로젝트, 파일 구조 파악

송지윤·2024년 4월 1일

Spring Framework

목록 보기
6/65

프로젝트 구조

  • src/main/java : 자바 소스 코드를 모아두는 폴더
  • src/main/resources : 자바에서 사용할 자원, 설정 파일을 모아두는 폴더
    - templates : Java 템플릿을 모아두는 폴더(thymeleaf)
    안에 index.html 만듦
    • static : templates에 적용될 정적 자원을 모아두는 폴더 (JS, CSS, 이미지)
    • application.properties : Spring Boot 프로젝트 관련 설정을 정의하는 파일
  • Project and External Dependencies : build.gradle에 명시된 라이브러리 모음
  • build.gradle : gradle(빌드도구 == maven의 pom.xml) 프로젝트엣 필요한 라이브러리, 배포, 빌드 관련 설정을 작성하는 파일

BoardProjectBootApplication.java (XXXApllication.java)

package edu.kh.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoProject1Application {

	public static void main(String[] args) {
		SpringApplication.run(DemoProject1Application.class, args);
	}

}
  • @SpringBootApplication : Spring Boot Application에 필요한 필수 어노테이션을 모아둔 어노테이션
    - Spring Boot 프로젝트로 만든 애플리케이션의 실행을 담당하는 클래스
    - Spring Application을 최소 설정으로 간단하고 빠르게 실행할 수 있게 해줌
    - Java 파일을 실행하듯이 Run 버튼을 클릭하면 배포가 시작됨

  • (exclude = {SecurityAutoConfiguration.class})
    - Spring Security 자동 설정을 사용하지 않음

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
  • @SpringBootConfiguration
    - Spring설정 파일(설정을 위해 만든 클래스)임을 나타내는 어노테이션
    - @Conficuration(해당 클래스는 자바 기반 설정 파일임을 나타내는 어노테이션) 자식 어노테이션

  • @ComponentScan
    - 지정된 패키지 이하에 bean 등록을 위한 어노테이션을 인식하는 어노테이션(bean 등록이 수행됨)
    - @SpringBootApllication이 붙은 클래스가 위치하고 있는 패키지 이하가 @ComponentScan 지정 패키지가 됨.
    (최상위 패키지 바로 아래 클래스가 위치해야함!)

  • @EnableAutoConfiguration
    - 자동 설정을 위해 사용되는 어노테이션
    - 등록된 bean과 조건에 따라 자동으로 설정을 적용
    - spring-boot-autoconfigure 라이브러리
    → spring.factories 파일에 자동 설정을 위한 클래스가 등록되어있음

bulid.gradle

// Gradle 빌드 도구를 이용하는 프로젝트를 관리할 때 사용하는 파일
// dependencies 에 필요한 라이브러리, 모듈 작성하면 mvnrepository 에서 자동으로 다운로드가 진행됨.


plugins {
	id 'java'
	id 'org.springframework.boot' version '3.2.4'
	id 'io.spring.dependency-management' version '1.1.4'
}

group = 'edu.kh'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '17'
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

// 외부 연결된 사이트 mvn 에서
// 아래에 작성된 모듈, 라이브러리를 다운로드 받아서 프로젝트에 자동으로 추가
// 해당 파일 변경 후에 프로젝트 우클릭 -> Gradle 탭 -> Refresh Gradle Project 클릭
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}
  • Gradle 빌드 도구를 이용하는 프로젝트를 관리할 때 사용하는 파일
  • dependencies에 필요한 라이브러리를 작성하면 mvnrepository에서 자동으로 다운로드가 진행됨.

controller 요청주소 연결

Java 객체 : new 연산자에 의해 Heap 영역에 클래스에 작성된 내용대로 생성된 것

instance : 개발자가 만들고 관리하는 객체

IOC 와 DI 개발자가 객체 만들고 생명주기 관리하는 게 아니라 Spring 이 해줌
Bean : Spring Container(Spring)가 만들고 관리하는 객체

package edu.kh.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

요청/응답을 제어할 컨트롤러 역할 명시 + Bean 으로 등록해주는 어노테이션(== 객체로 생성해서 스프링이 관리)

@Controller
public class TestController {

servlet 할 때는 extends HttpServlet @WebServlet("/test") 어노테이션 했는데
Spring 은 방법이 다름

기존 Servlet : 클래스 단위로 하나의 요청만 처리 가능
Spring : 메서드 단위로 요청 처리 가능

index.html /test 요청 받는 방법
@RequestMapping("요청주소")

  • 요청 주소를 처리할 메서드를 매핑하는 어노테이션 (클래스 단에서도 사용할 수 있음)

RequestMapping
1) 메서드에 작성

  • 요청주소와 메서드를 매핑
  • GET/POST 가리지 않고 매핑 (속성을 통해서 지정 가능)
@RequestMapping(value="test" , method=RequestMethod.GET)

(잘 사용하지 않음)
앞에 / 붙이지 않음 (특수한 몇몇 경우를 제외하고는 붙이지 않음)
@RequestMapping("test")

2) 클래스에 작성

  • 공통 주소를 매핑
    ex) /todo/insert, /todo/select, /todo/update 이런 요청들이 있다고 치면
    공통된 주소 /todo
    todo 라는 공통된 요청으로 받아서 한곳에서 처리(공통주소까지만 작성)
@RequestMapping("todo")
@Controller
public class TodoController {
	@RequestMapping("insert")	
	public String insert() {}

=> /todo/insert 매핑

@RequestMapping("select")
public String select() {}

=> /todo/select 매핑

@RequestMapping("update")
public String update() {}

=> /todo/update 매핑

Spring Boot Controller 에서 특수한 경우를 제외하고 매핑 주소 제일 앞에 "/"를 작성 안함
(관례)

/test 요청 시 처리할 메서드 매핑(GET/POST 가리지 않고)

	@RequestMapping("test")
	public String testMethod() {
		System.out.println("/test 요청 받음");

반환형 String 기본
Controller 메서드의 반환형이 String 인 이유

  • 메서드에서 반환되는 문자열이
    forward 할 HTML 파일 경로가 되기 때문

Thymeleaf : JSP 대신 사용하는 템플릿 엔진

Thymeleaf 에서 정해둔 접두사와 접미사가 있음
classpath: == src/main/resources
접두사 : classpath:/templates/
접미사 : .html

src/main/resources/templates/ .html
빈칸에 들어올 값만 작성해주면 됨
return "test"; // forward(접두사 + 반환값 + 접미사 경로의 HTML 파일로 forward)
반환될 경로만 작성
src/main/resources/templates/test.html

templates 폴더 안에 test.html 파일 만들면 됨
spring boot 는 서버 껐다 켰다 안해도 됨

사용 연습

ExampleController

Bean : 객체를 만드는 것부터 관리하는 것까지 Spring 이 하는 거 스프링이 만들고 관리하는 객체

요청 주소 매핑하는 방법

1) @RequestMapping("주소")

2) @GetMapping("주소") : GET (조회) 방식 요청 매핑

@PostMapping("주소") : POST (삽입) 방식 요청 매핑

@PutMapping("주소") : PUT (수정) 방식 요청 매핑

@DeleteMapping("주소") : DELETE (삭제) 방식 요청 매칭

HTTP 통신 CRUD
create read update delete

PC 에서 보내든 모바일로 보내든 똑같은 요청
REST API 자원을 이름으로 구분해서 자원의 상태를 주고받는 거 (Put 이나 Delete)
(멱등) 항상 똑같은 결과 get put delete

post 는 다른 결과 ex) 결제 2번 됨

package edu.kh.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller // 요청 / 응답 제어 역할인 Controller 임을 명시 + Bean 등록
public class ExampleController {
	
//	@RequestMapping("example")
	@GetMapping("example") // /example GET 방식 요청 매핑 (POST는 못 받음)
	public String exampleMethod() {

a 태그는 get 요청

forward 하려는 HTML 파일 경로 작성
단, ViewResolver 가 제공하는 Thymeleaf 접두사, 접미사 제외하고 작성

접두사 : classpath:/templates/
접미사 : .html

		return "example"; // src/main/resources/templates/example.html
	}
}
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>서버로 파라미터 제출하기</title>
</head>
<body>
    <h1>서버로 파라미터 제출하기</h1>

    <h3>1. HttpServletRequest.getParameter("key") 이용</h3>
    
    <form action="/param/test1" method="post">
        이름 : <input type="text" name="inputName"> <br>
        나이 : <input type="number" name="inputAge"> <br>
        주소 : <input type="text" name="inputAddress"> <br>

        <button>제출하기</button>
        <!-- 기본 submit -->
    </form>

    <hr>

    <h3>
        2. @RequestParam 어노테이션을 이용
        - 낱개(단수) 파라미터 컨트롤러에서 얻어오는 방법
    </h3>

    <form action="/param/test2" method="post">
        책 제목 : <input type="text" name="title"> <br>
        작성자 : <input type="text" name="writer"> <br>
        가격 : <input type="number" name="price"> <br>
		출판사 : <input type="text" name="publisher"> <br>

        <button>제출하기</button>
    </form>
	
    <hr>

    <h3>
        3. @RequestParam 어노테이션을 이용
        - 여러 개(복수) 파라미터 컨트롤러에서 얻어오는 방법
    </h3>

    <form action="/param/test3" method="post">
        색상 :
        Red <input type="checkbox" name="color" value="Red">
        Green <input type="checkbox" name="color" value="Green">
        Blue <input type="checkbox" name="color" value="Blue">
    
        <br><br>

        과일 :
        Apple <input type="checkbox" name="fruit" value="Apple">
        Banana <input type="checkbox" name="fruit" value="Banana">
        Orange <input type="checkbox" name="fruit" value="Orange">
    
        <br><br>
        상품명 : <input type="text" name="productName"> <br>
        유통기한 : <input type="date" name="expirationDate"> <br>

        <br><br>
        <button>제출하기</button>
    </form>
    
    <hr>

    <h3>4. @ModelAttribute를 이용한 파라미터 얻어오기</h3>

    <form action="/param/test4" method="post">
        ID : <input type="text" name="memberId"> <br>
        PW : <input type="password" name="memberPw"> <br>
        NAME : <input type="text" name="memberName"> <br>
        AGE : <input type="number" name="memberAge"> <br>

        <button>제출하기</button>
    </form>
</body>
</html>

ParameterController

package edu.kh.demo.controller;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import edu.kh.demo.model.dto.MemberDTO;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

// Bean : 스프링이 만들고 관리하는 객체
@Controller // 요청/응답 제어 역할 명시 + Bean 등록
@RequestMapping("param") // /param 으로 시작하는 모든 요청을 현재 컨트롤러로 매핑
@Slf4j // log를 이용한 메세지 출력 시 사용 (Lombok 라이브러리에서 제공)
public class ParameterController {

	// @RequestMapping("main")
	@GetMapping("main") // /param/main GET 방식 요청 매핑
	public String paramMain() {
		
		// classpath: src/main/resources
		// 접두사 : classpath:/templates
		// 접미사 : .html
		return "param/param-main";
		// -> src/main/resources/templates/param/param-main.html
	}

1. HttpServletRequest.getParameter("key") 이용

매개 변수 자리에 넣어줌 HttpServletRequest req
-> 이렇게 넣으면 사용할 수 있음

HttpServletRequest :

  • 요청 클라이언트 정보, 제출된 파라미터 등을 저장한 객체
  • 클라이언트 요청 시 생성

전달 인자 해결사
ArgumentResolver(전달 인자 해결사) (Spring 내장 객체)
생성해서 주입시켜줌(Spring 이)

  • Spring 의 Controller 메서드 작성 시 매개변수에 원하는 객체를 작성하면 존재하는 객체를 바인딩 또는 없으면 생성해서 바인딩
	
	@PostMapping("test1") // /param/test1 POST 방식 요청 매핑
	public String paramTest1(HttpServletRequest req) {
		// html 에서 넘겨준 값 받기
		String inputName = req.getParameter("inputName");
		int inputAge = Integer.parseInt(req.getParameter("inputAge"));
		// int 형으로 형 변환
		String inputAddress = req.getParameter("inputAddress");

System.out.println(inputName);
얻어온 값 이렇게 확인했었는데
디버깅 console 창에 log 띄워줄거임

debug : 코드 오류 해결
-> 코드 오류 없는데 정상 수행이 안될 때
(값이 제대로 넘어오는지 확인해봐야함)
=> 값이 어떻게 생겼는지 값 추적을 해야함
log 사용하겠다고 클래스 단에 알려줘야함

		log.debug("inputName : " + inputName);
		log.debug("inputAge : " + inputAge);
		log.debug("inputAddress : " + inputAddress);

하나 더 설정해줘야함
application.properties 에 설정 logging.level.edu.kh.demo=debug

Console 창 확인
inputName : 홍길동
inputAge : 20
inputAddress : 경기도 평택시

forward 경로 써주는 거

Spring 에서 Redirect(재요청) 하는 방법

  • Controller 메서드 반환 값에
    "redirect:요청주소"; 작성
		return "redirect:/param/main";
		// 공통주소인 param main 나머지 주소 매핑해주는 곳으로 감
	}

2. RequestParam 어노테이션을 이용 - 낱개 파라미터 얻어오기

  • request 객체를 이용한 파라미터 전달 어노테이션
  • 매개변수 앞에 해당 어노테이션을 작성하면, 매개변수에 값이 주입됨
  • 주입되는 데이터는 매개변수의 타입에 맞게 형변환/파싱이 자동으로 수행됨
    (Integer.ParseInt 이런 거 안해도 됨)

[기본 작성법]
@RequestParam("key") 자료형 매개변수명

[속성 추가 작성법]
@RequestParam(value="name", required="false", defaultValue="1")

value : 전달받은 input 태그의 name 속성값

required : 입력된 name 속성값 파라미터 필수 여부 지정(기본값이 true)
-> required = true 인 파라미터가 존재하지 않는다면 400 Bad Request 에러 발생

defaultValue : 파라미터 중 일치하는 name 속성값이 없을 경우에 대입할 값 지정.
-> required=false 인 경우 사용

	@PostMapping("test2")
	public String paramTest2(@RequestParam("title") String title,
			@RequestParam("writer") String writer,
			@RequestParam("price") int price,
			@RequestParam(value="publisher", required=false, defaultValue="열린책들") String publisher) {

title : 어린왕자
writer : 생택쥐베리
price : 10100
publisher : 열린책들

default 안 쓰면 publisher : null 이렇게 뜸

publisher 에 값 작성하면 작성한 값 넘어옴 publisher : 느낌표출판사

어노테이션 생략 안됨 에러남

		// 그냥 int로 가져올 수 있음
		log.debug("title : " + title);
		log.debug("writer : " + writer);
		log.debug("price : " + price);
		log.debug("publisher : " + publisher);

There was an unexpected error (type=Bad Request, status=400).
HTML 에서 넘기는 param 값 없을 때 이렇게 뜸

title : 어린왕자
writer : 생택쥐베리
price : 10000

		return "redirect:/param/main";
	}

3. @RequestParam 여러 개 파라미터

String[]
List<자료형>
Map<String, Object>

required 속성은 사용 가능하나,
defaultValue 속성은 사용 불가

	@PostMapping("test3")
	public String paramTest3(@RequestParam(value="color", required=false) String[] colorArr,
			@RequestParam(value="fruit", required=false) List<String> fruitList,
			@RequestParam Map<String, Object> paramMap
			) {

Map 사용할 때는 소괄호 없이 사용할 수 있음

		log.debug("colorArr : " + Arrays.toString(colorArr));
		log.debug("fruitList : " + fruitList);
		log.debug("paramMap : " + paramMap);

colorArr : [Red, Green, Blue]
fruitList : [Apple, Banana, Orange]
paramMap : {color=Red, fruit=Apple, productName=책, expirationDate=2024-04-02}

Map 형태로 받을 때는 키가 중복되면 앞에 하나만 나옴(배열형태 아님)
겹치지 않는 name 값들을 가지고 올 때 쓰기 좋음

@RequestParam Map<String, Object>
-> 제출된 모든 파라미터가 Map 에 저장됨
-> 단, key(name 속성값)이 중복되면 처음 들어온 값 하나만 저장된다.
-> 같은 name 속성값 파라미터 String[], List 로 저장 X

		return "redirect:/param/main";
	}

4. ModelAttribute 를 이용한 파라미터 얻어오기

@ModelAttribute

  • DTO(또는 VO)와 같이 사용하는 어노테이션

전달 받은 파라미터의 name 속성 값이
같이 사용되는 DTO의 필드명과 같으면
자동으로 setter 를 호출해서 필드에 값을 세팅

MemberDTO

@ModelAttribute 를 이용해 값이 필드에 세팅된 객체를 "커맨드 객체" 라고 부름

@ModelAttribute 사용 시 주의사항

  • DTO 에 기본생성자, setter 가 필수로 존재해야 한다.

@ModelAttribute 어노테이션은 생략이 가능

inputMember : MemberDTO(memberId=user02, memberPw=pass02, memberName=고길동, memberAge=53)
inputMember : MemberDTO(memberId=user03, memberPw=pass03, memberName=이순신, memberAge=60)

	@PostMapping("test4")
	public String paramTest4(/*@ModelAttribute*/ MemberDTO inputMember) {
		//                                                -> 커맨드 객체
		log.debug("inputMember : " + inputMember.toString());

inputMember : MemberDTO(memberId=user01, memberPw=pass01, memberName=홍길동, memberAge=20)

		return "redirect:/param/main";
	}
}

0개의 댓글