
간단한 URL 단축 서비스를 혼자 힘으로 구현할 수 있다.
-> 실습 위주
http://localhost:8090/g/3
http://localhost:8090/s/좋은 개발팁 영상/https://likelion.net~
g: go
s: shorten
짧은 url/s/url제목/모든url주소
스프링부트는 백엔드 프레임워크 입니다. 스프링부트는 그 자체로 완성된 하나의 웹 사이트 프로그램 입니다. 개발자는 스프링부트에 있는 빈칸만 적절히 채우면 됩니다. 그래서 스프링부트에 익숙해지기만 한다면 엄청나게 빠르게 웹 사이트(서비스) 개발을 할 수 있습니다. 인텔리제이 커뮤니티에서는 스프링부트 프로젝트를 만들 수 없습니다. 인텔리제이에서 lombok 플러그인 설치를 해야 합니다.
스프링 이니셜라이저 - 온라인 스프링부트 프로젝트 생성기를 이용한다.

add dependencies: 다양한 기능들이 미리 추가되어 있음
generate하면 생성되어 다운로드됨
explore로 미리보기 가능함
다운로드한 파일을 인텔리제이 안의 파일(EX: /IdeaProject/) 에 넣어 인텔리제이에서 파일 열기하면 프로젝트를 진행할 수 있다.
초기 설정: application.yml (yml파일로 수정)
server:
port: 8090
spring:
application:
name: demo01
운영환경(실제 서비스 환경)에서는 보통 클라이언트(손님, 브라우저)와 서버(주인, 스프링부트)는 다른 컴퓨터에서 실행됩니다. 클라이언트와 서버 사이에 통신이 필요합니다. 브라우저와 스프링부트에는 이미 통신기능이 구현되어 있습니다. 기본적으로 HTTP 방식으로 통신합니다. 스프링부트 프로젝트 안의 특정 클래스를 외부 고객과의 소통을 담당하는 컨트롤러로(창구 직원, 영업 사원 등에 비유될 수 있음) 만들어야 합니다.(@Controller) 컨트롤로의 모든 메서드를 외부(브라우저)에서 호출(URL 로 호출)할 수 있도록 만들 필요는 없습니다. @GetMapping("경로") 이런식으로 특정메서드를 외부에서 호출가능한 상태로 만들 수 있습니다. 즉 컨트롤러안의 메서드중에서 필요한 메서드만 골라서 액션 메서드로 만들 수 있습니다. 고객의 요청(브라우저에서 주소를 입력하고 엔터, 혹은 재요청(F5)) 시 마다 액션 메서드가 수행됩니다. 액션 메서드가 수행되고 return 값을 요청에 대한 응답으로 삼고싶다면(즉 리턴값이 브라우저에 표시되도록 하고 싶다면) @ResponseBody를 붙여야 합니다.
스프링부트는 고객입장에서 보면 원격지에 있습니다. 할 일 관리 서비스 처럼 고객이 직접 스프링부트가 실행되고 있는 컴퓨터에서 명령을 입력할 수 없습니다.
@Controller, @GetMapping, @ResponseBody 같은것들은 어노테이션으로 주석입니다. 사람이 읽기위한 주석이 아니라 자바가 이해하는 주석입니다. 스프링부트에게 우리(개발자)의 의도를 설명해주는 용도로 많이 사용됩니다.


HTTP는 편지를 이용해서 손님과 점원이 이야기를 나누는 방법에 대한 규칙 입니다.
고객 == 클라이언트 == 브라우저 == 서비스 이용자
점원(주인) == 서버 == 스프링부트 == 서비스 제공자
고객은 요청편지를 점원에게 보내고, 점원은 그것에 대한 응답편지를 고객에게 보냅니다. 편지는 헤더와 바디 부분으로 나뉩니다. 헤더에는 부가적인 정보가 담기고, 바디에는 내용이 담깁니다. 우리의 생각이 타인에게 전해지려면, 양자가 모두 이해할 수 있는 소리 라는 수단을 이용해야 합니다.
우리의 생각 => 입을 통해서 소리로 변환 => 타인이 소리를 귀로 듣고 => 생각으로 변환
타인이 응답을 할 때도 마찬가지의 과정이 진행됨 고객의 생각(데이터, 정보)가 점원에게 전해지려면, 양자가 모두 이해 할 수 있는 String(문자열) 이라는 수단을 이용해야 합니다.
브라우저의 데이터(정보) => 편지에 글로 작성 => 점인이 편지에 적인 글을 읽고 => 정보로 변환
점원이 응답을 할 때도 마찬가지의 과정이 진행됨
브라우저(고객)의 모국어는 자바스크립트 이고, 스프링부트(점원)의 모국어는 자바 입니다. 쉽게 말해서 브라우저는 자바의 int, boolean, char, float, 배열, 리스트, 맵을 이해할 수 없습니다. 스프링부트도 마찬가지로 브라우저의 number, 객체 등을 이해할 수 없습니다. 그 둘이 동시에 이해하는 데이터는 오직 String(글, 텍스트) 입니다.


HomeController.java
package com.ll.demo01;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HomeController {
@GetMapping("a")
@ResponseBody
public String hello(
String age,
String id
){
return "%s번 사람의 나이는 %s살 입니다.".formatted(id, age);
}
@GetMapping("b")
@ResponseBody
public String plus(
@RequestParam("a") String num1Str,
@RequestParam("b") String num2Str
){
int num1 = Integer.parseInt(num1Str);
int num2 = Integer.parseInt(num2Str);
System.out.println("a= " +num1);
System.out.println("b= " +num2);
return "a + b = %d".formatted(num1+num2);
}
}
http://localhost:8090/a?age=22&id=1

http://localhost:8090/b?a=10&b=20


build.gradle 에 implementation 'org.springframework.boot:spring-boot-starter-web' 덕분에 잭슨(Jackson) 이라는 라이브러리를 스프링부트에서 쓸 수 있습니다. 다른말로하면 org.springframework.boot:spring-boot-starter-web 에 잭슨이 포함되어 있습니다. 그렇기 때문에 org.springframework.boot:spring-boot-starter-web 를 build.gradle 에 기술한 것만으로 잭슨을 쓸 수 있습니다. 잭슨은 스프링부트에서 자동으로 작동합니다. 잭슨이 자동으로 작동하는 케이스는 2가지 입니다.
케이스 1 : 브라우저가 전해준 String(Text)를 자바가 이해할 수 있는 형태(int, String, boolean 등)으로 변환(자동으로 작동)
케이스 2 : 액션 메서드가 리턴한 데이터(자바가 이해할 수 있는 형태(int, String, boolean 등))가 브라우저에게 전송되기 전에 String(Text) 형태로 변환(자동으로 작동)
JSON 은 String 으로 자바와 자바스크립트 등에서 사용되는 다양한 객체를 표현할 수 있는 문법입니다. 보통 클라이언트와 서버가 통신할 때 JSON 을 많이 사용합니다. String 으로 객체를 표현하는 방법에 대한 표준이 보통 XML과 JSON이 있는데 JSON을 많이 사용합니다. Jackson은 액션 메서드가 String 이외의 형태의 데이터를 리턴하면 String 형태(그 중에서 JSON 방식)로 변경해줍니다.
XML과 JSON 비교
추가된 HomeController.java
@GetMapping("c")
@ResponseBody
public String c(
boolean married
){
return married ? "결혼" : "미혼";
}
@GetMapping("d")
@ResponseBody
public String d(
Boolean married
){
if (married == null) return "정보를 입력해주세요.";
return married ? "결혼" : "미혼";
}
public static class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
@GetMapping("person1")
@ResponseBody
public String person(
String name,
int age
){
Person person = new Person(name, age);
return person.toString();
}
@GetMapping("person2")
@ResponseBody
public String person(
Person person
){
return person.toString();
}
@GetMapping("e")
@ResponseBody
public int e(){
int age = 10;
return age;
}
@GetMapping("f")
@ResponseBody
public boolean f(){
boolean married = true;
return true;
}
@GetMapping("g")
@ResponseBody
public Person g(){
Person person = new Person( "Paul", 22);
return person;
}
@GetMapping("h")
@ResponseBody
public int[] h(){
int[] arr = new int[] {10, 20, 30};
return arr;
}
@GetMapping("i")
@ResponseBody
public List<Integer> i(){
List<Integer> arr = List.of(10,20,30);
return arr;
}
@GetMapping("j")
@ResponseBody
public Map<String, Object> j(){
Map<String, Object> person = new HashMap<>();
person.put("age", 23);
person.put("name", "Paul");
return person;
}
}
http://localhost:8090/c 실행

null 값이 들어가 있으므로 미혼이 뜬다.
http://localhost:8090/d 실행

null 값이므로 정보를 입력해달라고 뜬다.
http://localhost:8090/d?married=True 실행

결혼이 뜨는 것을 확인 할 수 있다.
http://localhost:8090/person1?name=jinsu&age=24 실행

http://localhost:8090/person2?name=jinsu2&age=25 실행

build.gradle에 롬복을 기술하는 것과 별개로 IDE(여기서는 인텔리제이)에 롬복 플러그인이 설치되어야 사용할 수 있다. 롬복을 사용하면 소스코드의 타이핑 양을 줄일 수 있다.

@Component 을 클래스에 붙이거나 @Bean 메서드를 통해서 빈(공유객체)이 라는 것을 스프링부트에 등록할 수 있다.

필요할 때 마다 @Autowired 나 final + @RequiredArgsConstructor 로 해당 빈을 가져와서 쓸 수 있다.

Lombok 에서 제공하는 @Builder 어노테이션은 new 대신, 빌더패턴으로 객체를 생성할 수 있게 해준다.


유용한 어노테이션
추가된 HomeController.java
@AllArgsConstructor
@Getter
@Builder
@ToString
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public static class Post {
@ToString.Exclude
@JsonIgnore
@EqualsAndHashCode.Include
private Long id;
private LocalDateTime createDate;
private LocalDateTime modifyDate;
@Builder.Default
private String subject = "제목 입니다.";
private String body;
}
@GetMapping("/posts")
@ResponseBody
public List<Post> getPost() {
List<Post> posts = new ArrayList<>() {{
add(new Post(1L, LocalDateTime.now(), LocalDateTime.now(), "제목 1", "내용 1"));
add(new Post(2L, LocalDateTime.now(), LocalDateTime.now(), "제목 2", "내용 2"));
add(new Post(3L, LocalDateTime.now(), LocalDateTime.now(), "제목 3", "내용 3"));
add(new Post(4L, LocalDateTime.now(), LocalDateTime.now(), "제목 4", "내용 4"));
add(new Post(5L, LocalDateTime.now(), LocalDateTime.now(), "제목 5", "내용 5"));
}};
return posts;
}
@GetMapping("/posts2")
@ResponseBody
public List<Post> getPost2() {
List<Post> posts = new ArrayList<>() {{
add(
Post
.builder()
.id(1L)
.createDate(LocalDateTime.now())
.modifyDate(LocalDateTime.now())
.subject("제목 1")
.body("내용 1")
.build()
);
add(
Post
.builder()
.id(2L)
.createDate(LocalDateTime.now())
.modifyDate(LocalDateTime.now())
.subject("제목 2")
.body("내용 2")
.build()
);
add(
Post
.builder()
.id(3L)
.createDate(LocalDateTime.now())
.modifyDate(LocalDateTime.now())
.subject("제목 3")
.body("내용 3")
.build()
);
}};
return posts;
}
@GetMapping("/posts/1")
@ResponseBody
public Post getPost1() {
Post post = Post
.builder()
.id(1L)
.createDate(LocalDateTime.now())
.modifyDate(LocalDateTime.now())
.subject("제목 1")
.body("내용 1")
.build();
System.out.println(post);
return post;
}
@GetMapping("/posts/2")
@SneakyThrows
@ResponseBody
public Post getPostv2(){
Post post = Post
.builder()
.id(2L)
.createDate(LocalDateTime.now())
.modifyDate(LocalDateTime.now())
.subject("제목 2")
.body("내용 2")
.build();
Thread.sleep(5000);
System.out.println(post);
return post;
}
http://localhost:8090/posts/1 실행

http://localhost:8090/posts/2 실행

5초 sleep후 실행된다.