
서버로 요청하는 프로그램을 모두 일컬어 말한다. 웹 브라우저가 대표적인 클라이언트 중 하나로 브라우저에 주소를 입력하고 정보를 요청하는 행위를 ‘서버에 요청한다’ 라고 표현할 수 있다.
클라이언트의 요청을 받아 처리하는 주체, 클라이언트가 데이터를 요청했다면 데이터를, 서버 내에서 처리만 해달라는 요청을 했다면 요청에 대한 처리만 할 수도 있다.
클라이언트에게 네트워크를 통해 정보나 서비스를 제공하는 컴퓨터 시스템으로 컴퓨터 프로그램 또는 장치를 의미한다. 라는 사전적인 정의가 있으며 서버는 클라이언트에게 무언가는 제공하는 역할인 것을 알 수 있다. 서버는 계속 대기하고 있다가 클라이언트의 요청이 있을 때 거기에 응답하는 어떤 장치 혹은 프로그램으로 간혹 ‘서버가 다운되었다’는 표현은 말 그대로 서버가 대기 상태가 아니어서 클라이언트의 요청에 응답할 수 없는 상태를 의미한다.

웹을 제공하는 역할을 한다. 웹은 크롬, 사파리와 같은 웹 브라우저를 통해 볼 수 있는 페이지다. 사용자가 웹 브라우저에 요청한 것을 알아채고 응답하는 역할을 하며, 웹 서버가 응답에 실어서 보낸 자원을(HTML, 이미지 등) 을 이용해서 웹 브라우저는 화면을 그린다.

사용자가 웹 브라우저를 통해 웹 서버에 요청하고 응답을 받을 때 정적인 컨텐츠 또는 동적인 컨텐츠를 가져올 수 있다.
데이터베이스를 사용하지 않아도 바로 웹서버에서 정보를 꺼내 응답할 수 있다.
웹 서버는 클라이언트의 다양한 요청을(동적인 컨텐츠) 처리하기 위해 비즈니스 로직이라는 것을 미리 준비해두며, 웹 서버는 여러 개의 비즈니스 로직이 합쳐진 것이라고 이해하면 된다.
비즈니스 로직을 만드는 방법은 다양하지만 협업을 위해 규칙, 규격이 필요했기 때문에 Java 프로그래밍 언어로 구현할 때의 규격은 Servlet 이라는 이름으로 가지게 되었으며, 자바를 이용해 웹 페이지를 동적으로 생상하는 백엔드 웹 애플리케이션 개발 기술을 Servlet 이라고 한다.
웹 서버를 동적인 컨텐츠, 정적인 컨첸츠를 처리하는 서버로 세분화 하면 Web Server는 정적인 컨텐츠를 처리하고, Web Application Server는 동적인 컨텐츠(비즈니스 로직)을 처리하는데 특화되어 있다.

웹서버는 클라이언트로부터 HTTP 요청을 받아들이고, 해당 요청에 대한 응답을 생성하여 전송하는 역할을 한다. 웹서버는 정적 파일을 제공하는 데 사용될 수 있고, 동적 컨텐츠를 생성하기 위해 웹 애플리케이션 서버와 연동되기도 한다.
동적인 컨텐츠를 생성하고 실행하는데 중점을 두며, 웹 어플리케이션을 실행하기 위한 비즈니스로직(Java코드) 은 Web Application Server에서 실행된다. 대표적인 WAS 중 하나로 Tomcat이 있으며, Tomcat은 자바 코드를 실행할 수 있는 서버 환경을 제공해준다.
웹 서버에서 실행되는 Java 클래스로 자바로 웹 애플리케이션을 개발하기 위해 필요한 기술이다. 웹 페이지를 동적으로 생산하는 백엔드 웹 애플리케이션 개발 기술을 Java로 구현해놓은 것이며, 클라이언트로부터 웹 서버에 요청이 오면 Java를 이용해서 클라이언트 요청을 처리하고 응답을 준다.
내부 동작을 Servlet 클래스에서 지원해주기 때문에 개발자가 비즈니스 로직 구현에 집중할 수 있게 해준다.

Client 요청을 WAS에서 처리 -> 서블릿 컨테이너 -> 서블릿 객체 생성/호출 -> Response 반환

서블릿의 생명주기 담당, HTTP 요청이 오면 서블릿을 생성, 응답을 주면서 종료해주는 역할을 한다.
서블릿을 지원하는 웹서버로 Jetty, Tomcat, JBoss 등등 종류가 많지만, 보통 Tomcat을 사용한다. Tomcat에서 비즈니스 로직이 실행되고 클라이언트에 응답 결과를 보낸다.

자바로 만들어진 프레임워크이며, 개발자가 쉽게 웹 어플리에이션을 만들 수 있도록 편리한 기능들을 제공한다.
Class object = new Class(); // 직접 제어
Spring이 제어할 수 있도록 → loC
기존에 사용했던 코드를 제거하고 새로운 코드로 바꿔끼우는 식의 유지보수를 진행하게 된다.
필드, 생성자를 선언하면 알아서 생성해준다.
개발자가 스스로 객체를 생성하지 않아도 스프링에서 알아서 객체를 컨트롤할 수 있도록 해주는 것을 제어의 역전이라고 한다. 인터페이스의 다형성을 활용한 부분이다. 제어의 권한을 개발자가 아니라 스프링한테 맡기는 방식이다.
스프링에서 객체를 생성해서 가지고 있다가 필요한 곳에 적절하게 주입해주는 것, 가져다가 쓰기 위해서 의존성 주입, 의존성 주입은 개발자가 선언해두면 스프링이 알아서 내부적으로 선언해준다.
class A{
B b = new B();
}
생성자 주입 방식으로 하면 스프링에서 @Service 되어있는 빈 객체를 가지고 있어서, 클래스 안에서 가져다 쓸 수 있게 알아서 주입을 해준다. 스프링이 helloService 객체를 들고 있다. 알아서 객체를 만들어놓고 들고 있다가 객체를 필요로 하는 부분이 있다면 주입해준다.
package com.estsoft.springprojecttest.service;
import org.springframework.stereotype.Service;
@Service // 스프링이 알아서 객체를 가지고 있게 됨
public class HelloService {
public String printHello(String param){
return "Hello " + param;
}
}
package com.estsoft.springprojecttest.controller;
import com.estsoft.springprojecttest.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
private HelloService helloService; // Dependency Injection (DI)
public HelloController(HelloService helloService) { // 생성자 주입 Autowired 지우고 작성, 위 코드랑 의도하는 바는 같음
this.helloService = helloService;
}
@GetMapping("/hello") // 특정 패턴이 왔을 때 처리할 수 있도록
// http://localhost:8080/hello?param=jo
public String hello(@RequestParam(value = "param", defaultValue = "Spring") String param){
// 객체 직접 생성, 호출
// HelloService helloService = new HelloService();
// return helloService.printHello(param);
// Spring 에게 제어권 맡기기(DI 사용해서)
return helloService.printHello(param);
}
}
직접적인 객체 생성을 하지 않고 생성자 주입 방식을 이용해 객체를 사용하게 될 것
클래스에 있는 필드를 외부 클래스에서 단순히 필드만 가져와 사용할 경우에는 평범하게 객체를 선언하여 사용하기도 한다. Member membe = new Member(1, “jo”, “address”) 특정 기능을 가져오는 게 아니기 때문에 가져온다. 보통 특정 기능을 가지고 있다면위와 같이 의존성, 생성자 주입 방식으로 진행한다. 모든 클래스에서 스프링에서 관리를 해주는 것은 아니다.
스프링 컨테이너는 앞서 본 것처럼 빈을 생성하고 관리하며, 빈이 생성/소멸되는 모든 생명주기를 스프링 컨테이너가 관리하고 @Autowired 같은 어노테시녕르 사용하여 빈을 주입받을 수 있게 DI를 지원해주기도 한다. 스프링 컨테이너에서 관리하고 생성한 객체를 been 이라고 한다.
public class A {
@Autowired
B b;
}
스프링은 빈을 스프링 컨테이너에 등록하기 위해 어노테이션 추가, 혹은 XML 파일 설정과 같은 방식을 사용하는데, 어노테이션을 사용하여 스프링 컨테이너에 등록하는 방식은 위 코드와 같다.
@Component // 클래스 MyBean을 스프링 컨테이너에 등록
public class MyBean {
}
MyBean 이라는 클래스에 Component 어노테이션을 붙이면 MyBean 클래스가 빈으로 등록된다. 등록된 이후에는 스프링 컨테이너에서 이 클래스를 관리하며 빈의 이름은 클래스 이름의 첫 글자를 소문자로 바꿔 관리한다. (MyBean → myBean 이라는 빈 이름으로 관리된다)
개발자가 어노테이션을 붙여서 빈 선언을 해놓고 스프링 어플리케이션이 동작하는 시점에 스캔하여 빈으로 등록해두어야 스프링 컨테이너에서 관리하는 대상이 될 수 있다. 대상이 빠져있다면 DI할 수 없다.
컴포넌트 스캔 대상이 되는 어노테이션
@RestController : 컴포넌트 스캔을 통해 Spring 빈 등록
@Controller
@Service : 컴포넌트 스캔을 통해 Spring 빈 등록
Portable Service Abstraction을 줄인 표현으로 환경의 변화와 관계없이 일관된 방식의 기술로의 접근 환경을 제공하는 추상화 구조를 말한다. 스프링에서 제공하는 다양한 기술들을 잘 추상화 해 놓았기 때문에 개발자가 인터페이스를 쉽게 이용하고 구현할 수 있다. PSA의 방식은 기술 교체의 용이성 장점을 가지고 있다. Spring이 다양한 기술 스택을 지원하면서도, 일관된 개발 방법을 제공할 수 있도록 하는 중요한 원리이다.
스프링에서 데이터베이스에 접근하기 위한 기술 DB 접근 기술
에서 어떤 기술을 사용하든 일관된 방식으로 데이터베이스에 접근하도록 인터페이스를 지원한다. 다른 기술을 사용해보고 싶을 때 필드만 변경 후 사용하는 형태의 클래스만 다시 정의해주면 된다. 이식 가능한 서비스의 추상화 개념이다. (= 인터페이스) 생성자만 바꿔 넣어줘도 전혀 다른 기술을 사용할 수 있다.
@Repository
public class UserRepository {
private final JdbcTemplate jdbcTemplate;
public UserRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public User findById(Long id) {
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new Object[]{id},
new BeanPropertyRowMapper<>(User.class)
);
}
}
JdbcTemplate을 사용하여 데이터베이스에서 User 정보를 조회하는 코드, JDBC와 관련된 복잡한 작업을 추상화하여, 데이터베이스 엑세스를 간편하게 해준다. JDBC API와 직접 상호작용 하지 않고도 데이터베이스 작업을 수행할 수 있다.
@Repository
public class UserRepository {
private final JdbcTemplate jdbcTemplate;
public UserRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public User findById(Long id) {
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new Object[]{id},
new BeanPropertyRowMapper<>(User.class)
);
}
}
Spring data JPA를 사용하여 User 정보를 조회하는 코드, EntityManager를 통해 데이터베이스 작업을 수행한다. JPA의 복잡한 세부 사항을 신경쓸 필요 없이 간단한 메서드 호출로 데이터베이스에 접근할 수 있다.
위 두 예제는 각각의 데이터 접근 기술을(JDBC와 JPA) 추상화한 예시로 PSA의 개념은 이러한 추상화 계층을 통해 구현되며, 특정 기술 스택(JDBC, JPA)에 종속되지 않고도 동일한 레이어에서 일관된 방식으로 애플리케이션을 개발할 수 있게 한다.