입문 Spring

정예진·2026년 4월 8일

Spring

목록 보기
2/19

2026.04.08

1-6 라이브러리와 프레임워크

📚 오늘 배운 것

라이브러리(Library)

라이브러리는 애플리케이션 개발에 필요한 클래스, 함수 등을 모아 놓은 코드의 모음을 의미함.

라이브러리 장점

개발자가 복잡한 코드를 직접 작성하지 않아서 개발 생산성이 높아짐.

라이브러리 단점

  • 업데이트 되지 않거나 지원이 중단될 경우 문제가 발생할 수 있음.
  • 버전 호환성 문제로 인해 다른 라이브러리나 기존 코드와 충돌이 발생할 수 있음. (생각보다 빈번하게 발생하는 문제)
  • 라이브러리의 내부 구현을 직접 수정하기 어려워서 특정 요구 사항에 맞게 조정하기 힘들 수 있음.

프레임워크(Framework)

프레임워크는 소프트웨어 개발을 간편하게 만들기 위한 소프트웨어 개발 환경임.

프레임워크는 일하기 위한 틀을 제공함. 개발자는 해당 틀 안에서 개발을 해야함.

프레임워크 장점

  • 일관된 구조를 제공하여 코드의 일관성과 가독성을 높여주며 팀 협업이 편해짐.
  • 기본적으로 필요한 기능과 도구를 제공해주기 때문에 개발자들이 핵심 비지니스 로직에 집중할 수 있음.
  • 통합된 테스트 환경과 도구를 제공하여 테스트를 쉽게 작성하고 실행할 수 있음.

프레임워크 단점

  • 프레임워크는 굉장히 복잡한 구조를 가지기 때문에, 익히는데 시간이 많이 소요됨.
  • 프레임워크의 새로운 버전이 기존 코드와 호환되지 않을 수 있음.
  • 정해진 규칙과 구조를 따르게 강제해서 자유롭게 변경하기 어려울 수 있음.

정리

프레임워크는 라이브러리들의 모음임.

프레임워크라는 틀 안에서 라이브러리들을 모두 호환 가능하도록 세팅을 해두었기 때문에 개발자는 더 빠르게 비즈니스 로직에 집중하여 빠르게 개발을 진행할 수 있음.

📌 프레임 워크


  • 애플리케이션을 개발할 때 전체적인 구조(frame)을 잡기 위해 사용하는 것
  • 기본적으로 프레임워크 내에서 사용하는 라이브러리들이 존재함

📌 라이브러리


  • 개발을 진행하는 과정에서 필요한 기능을 구현하기 위해 사용하는 것

스프링 프레임워크

Spring 프레임워크는 자바 플랫폼을 위한 오픈 소스 애플리케이션 프레임워크임. (간단하게 스프링이라고 부름.)

스프링을 사용하는 이유는 코드로 직접보면 체감할 수 있음.

  • Spring 사용 전
    import java.net.*;
    import java.io.*;
    
    public class RawSocketHandler {
        
        public void handleRequest(Socket clientSocket) throws IOException {
            BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream());
            
            try {
                String requestLine = in.readLine();
                if (requestLine == null) return;
                
                String[] requestParts = requestLine.split(" ");
                String method = requestParts[0];
                String path = requestParts[1];
                
                String headerLine;
                int contentLength = 0;
                while ((headerLine = in.readLine()) != null && !headerLine.isEmpty()) {
                    if (headerLine.startsWith("Content-Length:")) {
                        contentLength = Integer.parseInt(headerLine.substring(15).trim());
                    }
                }
                
                char[] bodyChars = new char[contentLength];
                in.read(bodyChars, 0, contentLength);
                String body = new String(bodyChars);
                
                String responseBody = "";
                String statusLine = "HTTP/1.1 200 OK";
                
                if ("/api/add".equals(path) && "POST".equals(method)) {
                    // JSON 파싱 - {"a": 10, "b": 20}
                    String json = body.replaceAll("[{}\"\\s]", "");
                    String[] pairs = json.split(",");
                    int a = 0, b = 0;
                    
                    for (String pair : pairs) {
                        String[] keyValue = pair.split(":");
                        if (keyValue[0].equals("a")) {
                            a = Integer.parseInt(keyValue[1]);
                        } else if (keyValue[0].equals("b")) {
                            b = Integer.parseInt(keyValue[1]);
                        }
                    }
                    
                    int result = a + b;
                    responseBody = "{\"result\":" + result + "}";
                    
                } else if (!"/api/add".equals(path)) {
                    statusLine = "HTTP/1.1 404 Not Found";
                    responseBody = "{\"error\":\"Not found\"}";
                } else {
                    statusLine = "HTTP/1.1 405 Method Not Allowed";
                    responseBody = "{\"error\":\"Method not allowed\"}";
                }
                
                out.println(statusLine);
                out.println("Content-Type: application/json");
                out.println("Content-Length: " + responseBody.length());
                out.println("Connection: close");
                out.println();
                out.print(responseBody);
                out.flush();
                
            } catch (Exception e) {
                String errorResponse = "{\"error\":\"Server error\"}";
                out.println("HTTP/1.1 500 Internal Server Error");
                out.println("Content-Type: application/json");
                out.println("Content-Length: " + errorResponse.length());
                out.println();
                out.print(errorResponse);
                out.flush();
            } finally {
                clientSocket.close();
            }
        }
    }
  • Spring 사용 후
    import org.springframework.web.bind.annotation.*;
    import java.util.Map;
    
    @RestController
    public class SpringCalculator {
        
        @PostMapping("/api/add")
        public Map<String, Integer> add(@RequestBody Map<String, Integer> numbers) {
            int result = numbers.get("a") + numbers.get("b");
            return Map.of("result", result);
        }
    }

이 처럼 스프링은 복잡한 것을 대신 해주는 편리한 도구임!


1-7 HTTP(1)

📚 오늘 배운 것

HTTP(HyperText Transfer Protocol)

HTTP는 웹에서 데이터를 주고받기 위한 통신 규약임.

편지를 주고받는 것과 비슷하게, 보내는 사람, 받는 사람, 내용이 정해진 형식으로 전달됨.

HTTP 동작 순서

클라이언트는 Request(요청)을 보내고, 응답을 기다림.

서버는 요청에 대한 처리를 수행 후 결과를 Response(응답)함.

HTTP 특징

무상태 (Stateless)

서버는 클라이언트의 상태(State)를 보존하지 않음.

📌 장점

Scale Out 수평 확장성이 높음.

갑자기 요청량이 증가하여도 서버를 증설하기 쉬움.

📌 단점

클라이언트가 데이터를 추가적으로 전송해야함.

📌 한계점

무상태로 설계할 수 없는 경우가 있음.

로그인은 어떻게 해야할까?

HTTP Message 구조

HTTP Message는 요청 메세지 , 응답 메세지 두 가지 종류가 있고 구조가 각각 다름.

HTTP Message 구조

HTTP 요청 메세지(Request Message)

  1. Start Line
    • HTTP Method
      • GET
      • 요청의 의도를 가진 GET, POST, PUT, PATCH 등이 있음
        • Create - POST
        • Read - GET
        • Update - PUT(전체), PATCH(일부)
        • Delete - DELETE
        • Request Target
    • path
      • 경로 : /event
      • Query String(= Query Parameter)에 해당하는 값도 포함함 (추후에 다시 설명함)
    • HTTP Version
      • 1.1
      • HTTP Version을 나타냄
  2. Header
    • Host: spartacodingclub.kr
    • 임의의 Header를 추가할 수 있음. (단, 서버가 값을 알고 있어야함.)
    • 요청의 추가 정보들을 가지고 있음.
  3. Empty Line (몰라도 됨.)
    • 공백 한 줄
    • 필수 값
  4. Message Body
    • 실제 전송하는 데이터가 담겨 있는 부분
      • HTML, 이미지, JSON 등 byte로 표현되는 모든 데이터 전송 기능
    • 요청 시 GET의 경우 Message Body가 지원되지 않는 경우가 많음. (잘 안씀.)

HTTP 응답 메세지(Response Message)

  1. Start Line
    • HTTP Version
    • Status Code
      • 요청이 성공했는지, 실패 했는지 나타내는 코드
    • Status Text
      • 코드와 함께 전달 되는 메세지
  2. Header
    • Response에서만 사용되는 Header 값들이 따로 존재함.
  3. Empty Line (몰라도 됨.)
    • 공백 한 줄, 필수 값
  4. Message Body
    • 실제 전송하는 데이터가 담겨 있는 부분
    • 만약 전송할 데이터가 없다면, Body가 공백으로 존재함.

HTTP Method

클라이언트 - 서버 사이에 이루어지는 요청, 응답 데이터를 전송하는 방식을 뜻함.

주요 Method

POST

  • 리소스 생성
  • 다른 Method로 처리하기 애매할 때 POST를 쓰기도 함.
  • Message Body를 통해 요청 데이터를 전달함.

GET

  • 리소스 조회
  • Message Body가 없음. (최근에는 Body를 넣을 수도 있긴 하지만 권장하지 않음)

PUT

  • 리소스 덮어쓰기

PATCH

  • 리소스 부분 수정

DELETE

  • 리소스 삭제

기타 Method

  • HEAD
    • GET에서 Message Body를 제외하고 상태 줄과 Header만 반환함.
  • OPTIONS
    - 대상 리소스에 대한 통신 가능한 Method를 설명함.

1-8 HTTP(2)

📚 오늘 배운 것

HTTP Method 속성

속성으로는 3가지가 있음.

  • 안전성(Safe)
  • 멱득성(Idempotent)
  • 캐시가능성(Cacheable)

안전성(Safe)

  • Get 메소드(조회)는 안전함.
    • 저장된 데이터를 변환하지 않음.
  • POST, DELETE, PUT, PATCH는 안전하지 않음.
    • 데이터를 생성, 수정, 삭제함.

멱득성(Idempotent)

  • 몇번을 호출해도 결과는 항상 같음.
    • GET → 같은 결과가 계속 조회됨.
    • PUT → 수정해서 대체된 후의 결과는 계속 같음.
    • DELETE → 같은 요청을 여러번해도 삭제된 결과는 같음.
    • POST → 멱등성을 보장하지 않음.
  • 요청을 실패한 경우 재시도 하기 위해 필요함.
    • 항상 결과가 같다면 마음껏 재시도 해도 됨.
    • 만약 멱등하지 않다면, 중복 요청을 보내서는 안됨.
    • 복구 매커니즘에 사용함.

캐시가능성(Cacheable)

  • 재사용을 위해 요청에 대한 응답을 저장할 수 있는가?
    • GET, HEAD, POST 메소드는 캐시가 가능함.
    • 일반적으로 GET, HEAD 정도만 캐시로 사용함.

캐시(Cache)란

클라이언트가 서버에 한번 요청했던 데이터에 대해서 매번 요청할 때마다 다시 전송할 필요가 없도록 임시적으로 데이터를 보관하고 있는 장소임.

HTTP 상태 코드

  • 2xx (성공)
    • 200 OK : 요청 성공
    • 201 Created : 새로운 리소스 생성
    • 202 Accepted : 요청이 수신되었으나 데이터 처리가 완료되지 않음
    • 204 No Content : 요청은 성공했지만, 응답 데이터가 없음
  • 3xx (리다이렉션) - OAuth 구현 시 많이 볼 수 있음 (카카오 로그인, 구글 로그인 등)
    • 요청을 완료하려면 추가 행동이 필요한 상태
  • 4xx (클라이언트 에러)
    • 400 Bad Request : 클라이언트가 HTTP 요청 내용을 수정 후 보내야함
    • 401 Unauthorized : 클라이언트가 리소스에 대한 인증(Authentication)이 필요함. (로그인같은거)
    • 403 Forbidden : 서버가 요청을 받았지만 승인 거부
    • 404 Not Found : 요청한 리소스가 서버에 없음. (이것을 이요해서 리소스를 숨겨놓기도 함. 있는데 없는척 가능)
  • 5xx (서버 에러)
    • 500 Internal Server Error : 대부분 500으로 처리함
    • 503 Service Unavailable : 서비스 이용 불가

1-9 API 이해하기

📚 오늘 배운 것

API 개념

API (Application Programming Interface) 는 프로그램끼리 대화하는 방법임.

클라이언트와 서버는 Interface의 명세대로만 통신하는 것이 바로 API임.

RESTful API

REST는 서버 구현을 알지 못하는 클라이언트가 API를 잘 이용할 수 있을지에 대한 고민의 결과임. (그래서 반드시 지켜야하는 규칙은 아님.)

RESTful API 디자인 원칙

1. 동사보단 명사를, 단수보단 복수를

❌ /member/get/item/hello

✅ /members/items

2. 마지막에 / 넣지 않기

❌ /members/

✅ /members

3. _ 대신 - 사용 + 대문자 사용하지 않기

❌ /restful_services

✅ /restful-services

4. 확장자 포함하지 않기(svg, png, exe 등)

❌ /image.svg

✅ /images

5. 계층화 하기

❌ /items/{memberId}/members/{itemId} (x)

✅ /members/{memberId}/items/{itemId}


1-10 Annotation과 Lombok

📚 오늘 배운 것

Annotation

설명이 어려워서 예시로 확인하는게 쉬움.

대표적인 예시로 인터페이스에서 봤던 @Override 가 있음.

public interface Mouse {

    void leftClick();
    void rightClick();
    void scrollWheel();
}
public class LogitechMouse implements Mouse {
    @Override
    public void leftClick() {
        // 로지텍 좌클릭 로직 구현
    }

    @Override
    public void rightClick() {
        // 로지텍 우클릭 로직 구현
    }

    @Override
    public void scrollWheel() {
        // 로지텍 스크롤휠 로직 구현
    }
}

간단하게 정리하면 어노테이션을 활용하면 복잡한 기능도 쉽게 사용할 수 있음!

Lombok 라이브러리

롬복은 게터나 세터같은 메서드들을 작성하지 않고 어노테이션으로 코드의 가독성이나 유지보수성을 높여줌. 눈으로 보기에도 코드가 깔끔해짐!!

주요 Lombok Annotation

@getter @setter

이 녀석이 코드가 가장 보기 싫게 만들어주는 듯. 게터와 세터를 하나씩 메서드를 작성하지 않고 속성부분 위에 어노테이션을 달아주면 자동으로 게터와 세터를 생성해서 사용할 수 있게 만들어줌.

@ToString

이녀석은 객체를 String으로 변환해주는 역할을 하는데 자동으로 toString() 메서드를 자동으로 생성해줌.

@NoArgsConstructor, @AllArgsConstructor, @RequiredArgsConstructor

@NoArgsConstructor 이것은 기본 생성자를 생성해줌.

@AllArgsConstructor 이건 모든 필드를 매개변수로 하는 생성자를 생성해줌.

@RequiredArgsConstructor 이건 필수(final) 필드만을 매개변수로 하는 생성자를 생성해줌.

@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String name;
    private int age;
}

@Slf4j

이 녀석은 클래스에 로그를 남기기 위한 Logger 객체를 자동으로 생성해줌.

@Slf4j
public class UserService {
    public void logMessage() {
        log.info("This is a log message");
    }
}

1-11 스프링 프로젝트 시작 & 의존성 관리(Gradle/Maven)

📚 오늘 배운 것

Spring Initializr

Spring Initializr(스프링 이니셜라이저)는 스프링 부트 기반의 프로젝트를 쉽고 빠르게 생성할 수 있도록 도와주는 웹 기반 도구 및 서비스임.

근데 이건 인텔리제이 유료버전을 사용하는 나에게는 필요 없음 ㅋ

스프링 프로젝트를 만드는 법은 우선 인텔리제이 유료버전을 키고 새로운 프로젝트 생성을 해줌.

왼쪽에 자바가 아닌 스프링 부트를 선택하고 이름이나 언어 타입 등은 사진이랑 똑같이 맞춰줌.

그리고 다음 누르고,

Lombok 이랑 Spring Web 추가 해주고 만들어주면 끝~!

여기서 Lombok 이랑 Spring Web을 추가 한 것이 의존성임!

이 의존성을 이미 만들어진 프로젝트에 추가 할 수도 있음. 예를 들어서 SpringDoc OpenAPI Starter WebMVC UI 라는 라이브러리를 의존성에 추가 하고 싶다고 한다면

https://mvnrepository.com/

이 사이트로 들어가서 SpringDoc OpenAPI Starter WebMVC UI 이걸 검색해줌.

클릭해서 들어가면 버전을 선택할 수 있는게 뜸.

여기서 가장 최신 버전을 눌러서 들어가보면

Gradle을 선택해서 나오는 URL을 복사해서

인텔리제이 스프링 프로젝트에 build.gradle 안에 이렇게 붙여넣기 해주면 됨!

그럼 오른쪽 위에 코끼리가 뜸!!

이 코끼리를 눌러면 끝! (코끼리를 꼭 눌러줘야 의존성에 추가가됨.)


1-12 Servlet 개념

📚 오늘 배운 것

서블릿(Servlet)

서블릿은 서버에서 실행되서 클라이언트의 요청을 처리하고, 그 결과를 동적으로 생성하여 응답하는 자바 프로그램임.

즉, 정적인 데이터가 아니라, 요청에 따라 다른 결과를 만들어내는 똑똑한 자바 클래스!

서블릿의 역할

  • 클라이언트가 보낸 데이터(HTTP request)를 읽고 해석
  • 조회, 계산 등 비지니스 로직을 수행
  • 결과를 생성하여 클라이언트에게 보냄
  • jakarta.servlet.http.HttpServlet 클래스를 상속받아 구현

서블릿 컨데이너(Servlet Container)

그냥 만든 서블릿 클래스는 스스로 실행 될 수 없기 때문에 이 서블릿을 실행하고 관리하고 웹 서버랑 통신하게 해주는 전문적인 환경이 서블릿 컨테이너임!

대표적인 서블릿 컨데이넉가 톰캣(Tomcat)임.

서블릿 컨테이너의 역할

  • 서블릿의 생명주기(생성, 실행, 소멸) 관리
  • HTTP 요청을 받아 서블릿에게 전달
  • 서블릿의 응답을 받아 클라이언트에게 전송
  • 동시에 여러 요청이 들어와도 문제없이 처리

서블릿 사용 시 클라이언트와 서버 간의 요청 & 응답 흐름

  1. HTTP 요청이 옴.
  2. 서버는 Request 와 Response 객체를 새로 만들어서 서블릿 객체를 호출함.
  3. 서블릿 객체는 Request 객체에서 요청 정보를 꺼내서 비지니스 로직을 수행함.
  4. 다시 Response 객체에 응답 정보를 담아서 클라이언트에게 응답으로 내려줌.

이 과정에서 개발자가 하는 일은 Request 객체에서 요청 정보를 꺼내 비지니스 로직을 수행하고 Response 객체를 만들기

API 구현 실습

앞에서 만들었던 프로젝트에서 사진과 같은 경로로 HelloServlet 클래스를 만들어주고 아래 코드를 복붙해줌.

import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 요청 정보는 필요없으므로 요청 정보를 꺼내지는 않습니다.
        // 비즈니스 로직 또한 따로 없으므로 작성하지 않습니다.
        resp.setContentType("text/plain; charset=UTF-8"); // 응답 정보 타입 설정
        resp.getWriter().print("hello"); // Response 객체에 응답 정보인 'hello'를 담아줍니다.
    }
}

그리고 메인 클래스에 @ServletComponentScan 이걸 넣어줌.

그리고 프로그램을 실행해보면

알 수 없는 요상한게 뜸.

여기서 포스트맨을 사용해서 열어볼거임!

포스트맨에서 새로운 컬렉션 생성해주고 hello라고 만들었음.

그 안에 add request 눌러서 자동으로 조회할 수 있는 API가 만들어짐 거기에 기본으로 설정되있는 아래 주소를 넣고

http://localhost:8080

뒤에 /hello를 붙여서

이렇게 넣어줌.

그러고 저장하고 Send 누르면

이렇게 hello가 잘 나오는 걸 볼 수 있음!


1-13 Servlet 기반 영화 추천 API 실습

📚 오늘 배운 것

API 명세서

레스토랑을 예시로 다시 설명을 해보자면 손님이 클라이언트, 주방이 서버 인데 손님이 주문을 하려면 메뉴판이 있어야함.

이때 API 명세서가 메뉴판의 역할임. (API 문서라고도 함.)

API 명세서의 핵심 구성 요소

  • API 개요 : 이 API가 어떤 목적으로 만들어졌는지, 버전 정보, 전체 주소(Base URL) 등
  • Endpoint : AP가 제공하는 개별 기능의 주소 (URL)
  • Method : 해당 주소로 어떤 작업을 할지 결정 (HTTP Method)
  • Parameters : 요청할 때 꼭 보내야 하는 추가 정보들
  • Request Body : 생성하거나 수정할 데이터의 실체 (주로 JSON 형식)
  • Response : 요청 처리 후 서버가 돌려주는 응답 정보
  • Status Codes : 요청의 성공/실패 여부를 나타내는 숫자 코드

영화 추천 API 만들기

  1. API 명세서 작성

    APIParametersRequest BodyResponseStatus
    GET /movies/recommendation--{ “title”: “쇼생크 탈출” }200
  2. 코드 작성

    import tools.jackson.databind.ObjectMapper;
    import jakarta.servlet.annotation.WebServlet;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    
    import java.io.IOException;
    import java.util.List;
    import java.util.Map;
    
    @WebServlet("/movies/recommendation")
    public class MovieRecommendationServlet extends HttpServlet {
    
        private final List<String> recommendedMovies = List.of(
                "쇼생크 탈출",
                "대부",
                "다크 나이트",
                "인생은 아름다워"
        );
    
        // 1. Map을 JSON으로 바꾸기 위해 필요한 ObjectMapper를 생성합니다.
        private final ObjectMapper objectMapper = new ObjectMapper();
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
    
            // 2. 랜덤으로 영화 하나를 선택합니다.
            int randomIndex = (int) (Math.random() * recommendedMovies.size());
            String randomMovie = recommendedMovies.get(randomIndex);
            Map<String, String> movieMap = Map.of("title", randomMovie);
    
            // 3. 응답 속성을 설정합니다.
            response.setContentType("application/json; charset=UTF-8");
    
            // 4. ObjectMapper를 사용해 JSON 문자열로 변환합니다.
            String json = objectMapper.writeValueAsString(movieMap);
    
            // 5. 생성된 JSON을 클라이언트에게 응답으로 보냅니다.
            response.getWriter().print(json);
        }
    }
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    
    @ServletComponentScan
    @SpringBootApplication
    public class DemoApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(DemoApplication.class, args);
    	}
    }

이렇게 코드를 넣어주고 포스트맨으로 가서 테스트 해보겠음.

주소 입력 칸에는

http://localhost:8080/movies/recommendation

이렇게 넣어주고 실행을 시켜보면 랜덤으로 저장된 영화 이름이 뜨는걸 볼 수 있음.


0개의 댓글