| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
- 디자인챌린지
- UXUI챌린지
- 백엔드개발자
- 백준
- Java
- Spring
- Be
- JPA
- OPENPATH
- 객체지향
- 국비지원교육
- 국비지원
- 오픈패스
- baekjoon
- 내일배움카드
- KDT
- 티스토리챌린지
- 패스트캠퍼스
- mysql
- 국비지원취업
- UXUI기초정복
- 백엔드 부트캠프
- 환급챌린지
- 디자인강의
- 오픈챌린지
- 자바
- 디자인교육
- 오블완
- 부트캠프
- UXUIPrimary
- Today
- Total
군만두의 IT 개발 일지
[스터디7] 09. 아키텍처 방식 및 XML, HTTP, JSON 본문
목차
부록 A
A.1 아키텍처 방식
A.1.1 모놀리식 방식
- 모놀리식(monolithic) 또는 모놀리스(monolith): 배포하고 실행하는 하나의 구성 요소로만 구성되어 있다는 것. 이 구성 요소에 모든 기능도 구현된다.
- 예) 서점 관리 앱 - 사용자는 서점에서 판매하는 제품, 송장, 배송, 고객을 관리한다. 모든 기능이 동일한 프로세스 일부로 동작한다.
- 비즈니스 흐름(flow): 사용자가 애플리케이션에서 수행하려는 것.
- 예) 서점 주인이 책을 판매할 때 흐름은 다음과 같다. 제품(products) 기능은 재고에서 책을 예약하고, 청구(billing) 기능은 해당 책의 송장을 생성하며, 배송(deliveries)은 언제 책을 배달할지 계획하고 고객(customers) 기능에 알린다.


- 수평 확장(horizontal scaling): 여러 시스템에서 동일한 앱을 실행하여 앱이 더 많은 요청을 처리할 수 있는 방식. 실행 중인 애플리케이션의 다수 인스턴스가 요청을 분할하여 시스템이 더 많은 부하를 처리할 수 있다
- 배경: 사용자 수의 변화와 데이터 증가를 처리하고 앱에는 더 많은 자원이 필요했고, 하나의 프로세스만 사용하면 자원 관리가 더 어려워졌다.
- 예) 단순히 선형적으로 증가한다고 가정해서 실행 중인 하나의 앱 인스턴스가 동시 요청을 5만 건 처리할 수 있다면, 앱 인스턴스 3개는 동시 요청 15만 건에 응답할 수 있어야 한다.
- 모놀리식 앱에서는 작은 변경 사항이라도 발생하면 전부 다시 배포해야 한다. 마이크로서비스 아키텍처를 사용하면 변경된 부분만 서비스를 다시 배포하면 된다.
- 모놀리식 앱을 유지 관리하기 어렵게 만드는 주요 원인은 지저분한 코드일 가능성이 높다. 또는 개발자가 책임을 뒤섞고 제대로 추상화하지 않았기 때문에 앱 유지 관리가 어려울 수 있다.
A.1.2 서비스 지향 아키텍처
- 앱 아키텍처를 모놀리식에서 서비스 지향 아키텍처(SOA)로 변경하면, 제품(products) 기능만 확장하고 다른 기능은 확장하지 않는 것이 가능하다. SOA에서는 모든 기능을 하나의 프로세스에서 사용하는 대신 여러 프로세스에서 기능을 구현한다.
- 배경: 모놀리식 앱에서는 앱 일부만 확장하도록 결정할 수 없다. 자원을 더 효율적으로 관리하려면 실제로 더 많은 자원이 필요한 기능만 확장하고 다른 기능은 확장하지 않아야 한다.
- 예) 도서 판매 앱 - 이 앱에는 제품(products), 배송(deliveries), 청구(billing), 고객(customers)이라는 네 가지 주요 기능이 있다. 어떤 기능은 더 복잡하거나 더 자주 사용되기 때문에 다른 기능보다 자원을 더 많이 소비한다.
- 장점: 책임을 더 잘 분리할 수 있다. 구현을 분리하고 일관성을 유지하기가 쉽다. 시스템을 유지 관리하는 데 도움이 된다. 여러 팀이 동일한 앱에서 함께 작업하는 대신 해당 팀별로 다른 서비스에서 작업할 수 있으므로 시스템에서 작업하는 팀을 관리하기도 쉽다.

- SOA를 사용할 때 이슈
- 서비스 간 통신
- 보안
- 데이터 영속성
- 배포
- 서비스 간 통신으로 발생한 복잡성
- 비즈니스 로직 흐름을 구현하려면 기능을 동기화할 필요가 있다. 모놀리식 방식에서 기능은 동일 앱의 일부였기 때문에 두 기능을 메서드 호출로 연결하는 것이 쉬웠다. 하지만 서로 다른 프로세스로 분리되면서 기능들은 네트워크로 통신해야 한다. 개발자들은 이 문제를 해결하려고 호출 반복, 회로 차단기(circuit breaker), 캐시(cache) 같은 다양한 기법이나 패턴을 사용한다.
- 서비스 간 통신을 설정하는 다양한 방법이 있다. REST 서비스, GraphQL, SOAP, gRPC, JMS 메시지 브로커, 카프카(Kafka) 등을 사용할 수 있다. 상황에 따라 한 가지 이상의 방법을 택하는 것이 적합할 수 있다.

- 시스템 보안에 추가된 복잡성
- 기능을 독립된 서비스로 분리함으로써 보안 구성은 복잡해졌다. 서비스들은 네트워크를 통해 메시지를 교환하므로 이 과정에서 정보가 노출될 수 있다.
- 교환되는 데이터(비밀번호, 은행 카드 정보, 기타 개인 정보 등) 중 일부는 못 보게 하기 위해 이런 정보는 전송 전에 암호화되어야 한다. 한 구성 요소에서 다른 구성 요소로 정보가 이동하는 동안 교환되는 상세 내용을 누군가 볼 수 있을지 여부는 상관없더라도, 누구도 그 내용을 변경할 수 없길 원한다.

- 데이터 영속성에 추가된 복잡성
- 대부분의 앱은 데이터를 저장할 방법이 필요하다. 데이터베이스는 앱에서 영속성을 구현하는 데 널리 사용되는 방식이다. 모놀리식 방식에서는 앱에 데이터를 저장하는 데이터베이스가 하나뿐이다. 클라이언트, 백엔드, 영속성을 위해 사용되는 데이터베이스로 구성된 계층이 3개이기 때문에 이를 3티어 아키텍처(three-tier architecture)라고 한다.
- SOA에서는 데이터를 저장해야 하는 서비스가 여러 개 존재한다. 각 서비스마다 개별 데이터베이스를 갖는 것은 어려움이 존재한다. 단일 데이터베이스로 데이터 일관성을 확보하는 것이 훨씬 쉽다. 독립된 데이터베이스가 늘어날수록 모든 데이터의 일관성을 유지하는 일은 어려워진다.

- 시스템 배포에 추가된 복잡성
- 시스템 배포에 복잡성이 늘어난다. 서비스가 더 많아졌지만 네트워크 인터페이스도 늘어난다. 시스템 보안을 위해 더 많은 설정이 필요하다는 점을 고려하면, 시스템 배포의 복잡성이 얼마나 더 증가하는지 알 수 있다.
A.1.3 마이크로서비스에서 서버리스까지
- 마이크로서비스: SOA에 대한 특정 구현 방식으로, 일반적으로 하나의 책임을 갖고 자체적인 영속성 기능을 한다(데이터베이스를 공유하지 않는다).
- 소프트웨어 아키텍처는 앱 기능에 관한 것만이 아니다. 시스템 작업 팀의 방식뿐만 아니라 시스템이 배포되는 방식에 맞추어 시스템의 아키텍처를 적용시키는 것도 포함된다.
- 서비스를 작게 만드는 기술은 오늘날 앱 코딩 및 좀더 간단한 기능을 구현하고 환경에 배포할 수 있을 정도로 발전했다. HTTP 요청, 타이머, 메시지 같은 이벤트가 이 기능을 트리거하고 실행하게 한다. 우리는 이런 구현을 서버리스(serverless) 함수라고 한다. '서버리스'는 함수 서버에서 실행되는 것을 의미하지 않는다.
A.2 컨텍스트 구성에 XML 사용
- 예전에 웹 개발자들은 일반적으로 컨텍스트와 스프링 프레임워크를 설정하는 데 XML을 사용했다.
- 오늘날에 개발자들은 수년 전에 XML 코드 구성이 불가능한 이유로 XML 설정 사용하지 않게 되었고, 어노테이션을 사용하는 것으로 대체했다. XML 장점이 있지만, 앱의 가독성과 유지 보수를 위해서는 어노테이션을 사용하는 것이 훨씬 더 쉽다.
구성에 XML을 사용하는 기법으로 스프링 컨텍스트에 빈을 추가하는 방법은 다음과 같다. XML을 사용하면 별도의 구성 파일이 필요하다. 이 파일 이름은 config.xml이라고 지정하고 모든 프로젝트의 resources 폴더에 추가한다.
<xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="parrotl" class="main.Parrot">
<property name="name" value="Kiki" />
</bean>
</beans>
Main 클래스에서는 스프링 컨텍스트를 대표하는 인스턴스 생성하고, 콘솔에서 앵무새 이름을 참조하여 이름을 출력함으로써 스프링이 빈을 성공적으로 추가했는지 테스트할 수 있다. ClasspathXmlApplicationContext 클래스를 사용하여 스프링 컨텍스트 인스턴스를 생성할 때는 XML 구성을 담고 있는 config.xml 파일 위치도 제공해야 한다.
public class Main {
public static void main(String[] args) {
var context = new ClassPathXmlApplicationContext("config.xml");
Parrot p = context.getBean(Parrot.class);
System.out.println(p.getName());
}
}
앱을 실행하면 XML 구성에서 앵무새 인스턴스에 부여한 이름(kiki)이 콘솔에 출력된다.
A.3 HTTP 기초 이해
왜 스프링에 관한 HTTP를 배워야 할까? 오늘날 스프링과 같은 애플리케이션 프레임워크로 구현하는 대부분의 앱은 웹 앱이고, 웹 앱은 HTTP를 사용하기 때문이다.
A.3.1 HTTP란 무엇인가?
- HTTP: 웹 앱에서 클라이언트가 서버와 통신하는 방식. 클라이언트-서버 컴퓨팅 모델을 사용하는 상태를 유지하지 않는 무상태(stateless) 텍스트 기반의 요청-응답 프로토콜.

A.3.2 클라이언트와 서버 간 HTTP 요청
스프링 앱에서는 클라이언트가 서버로 데이터를 보내려면 HTTP 요청을 사용해야 한다. 클라이언트를 구현할 때는 HTTP 요청에 데이터를 추가해야 한다. 서버를 구현할 때는 요청에서 데이터를 가져와야 한다.
HTTP 요청에서 고려할 사항
- 요청 URI(request URI): 클라이언트가 경로(path)를 사용하여 서버에 요청하는 자원을 알린다. 요청 URI 예시는 http://www.manning.com/books/spring-in-action/chapter-10와 같다.
- 요청 메서드(request method): 클라이언트가 요청한 자원에 대해 수행할 행동을 나타내는 동사(verb)다. 클라이언트는 GET, POST, PUT, DELETE 같은 메서드를 사용하여 HTTP 요청을 전송할 수 있다.
- 요청 매개변수(request parameter)(비필수): 클라이언트가 요청과 함께 서버로 보내는 소량의 데이터다. 대략 10~50자 정도를 표현할 수 있다. 요청 매개변수(쿼리 매개변수)는 쿼리 표현식을 추가하여 URI에 전송된다.
- 요청 헤더(request header)(비필수): 요청 헤더에서 소량으로 전송되는 데이터다. 요청 매개변수와 달리 이 값은 URI에서 보이지 않는다.
- 요청 본문(request body)(비필수): 클라이언트가 요청과 함께 서버로 전송하는 대량의 데이터다. 클라이언트가 문자 수백 개로 구성된 데이터를 보낼 때 HTTP 본문을 사용할 수 있다.
다음 코드는 HTTP 요청에서 세부 정보를 보여 준다.
POST /servlet/default.jsp HTTP/1.1 -> 요청은 메서드와 경로를 지정한다.
Accept: text/plain; text/html <- 값이 포함된 다양한 헤더를 요청 데이터로 추가할 수 있다.
Accept-Language: en-gb
Connection: Keep-Alive
Host: localhost
Referer: http://localhost/ch8/SendDetails.html
User-Agent: Mozilla/4.0 (MSIE 4.01;Windows 98)
Content-Length: 33
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate <- 값이 포함된 다양한 헤더를 요청 데이터로 추가할 수 있다.
lastName=Einstein&firstName=Albert <- 요청 매개변수를 사용하여 요청 데이터를 전송할 수 있다.
URI 형식은 다음 코드에서 볼 수 있다.
- <server_location>: 서버 앱이 실행되는 시스템의 네트워크 주소
- <application_port>: 실행 중인 서버 앱 인스턴스를 식별하는 포트 번호
- <resource_path>: 개발자가 특정 자원과 연관된 경로
http://<server_location>:<application_port>/<resource_path>

클라이언트가 요청에서 자원을 식별하면 HTTP 요청 메서드(request method)라 하는 동사를 사용하여 그 자원에 대해 수행할 작업을 명시한다. 웹 애플리케이션에서 가장 자주 접하는 HTTP 메서드는 다음과 같다.
- GET: 클라이언트가 서버에서 데이터를 가져오려고 할 때 사용한다.
- POST: 클라이언트가 서버에 데이터를 추가하려고 할 때 사용한다.
- PUT: 클라이언트가 서버의 데이터를 변경하려고 할 때 사용한다.
- DELETE: 클라이언트가 서버에서 데이터를 제거하려고 할 때 사용한다.
자주 사용되지 않더라도 중요한 다른 HTTP 메서드들이 있다.
- OPTIONS: 서버가 요청에 대해 지원하는 매개변수 목록을 반환하도록 지시한다. OPTIONS 메서드를 사용하는 가장 흔한 기능은 보안 구현과 관련된 CORS(Cross-Origin Resource Sharing)이다.
- PATCH: 서버 뒷단에서 특정 자원을 나타내는 데이터 일부만 변경될 때 사용될 수 있다. HTTP PUT은 클라이언트가 특정 자원을 완전히 대체하거나 갱신할 데이터가 없거나 추가할 때만 사용된다. 개발자들은 대부분 PATCH를 나타내는 경우에 HTTP PUT을 사용하는 경향이 높다.
클라이언트는 요청 매개변수(request parameter), 요청 헤더(request header), 요청 본문(request body)을 이용하여 서버로 데이터를 보낼 수 있다. 요청 매개변수와 요청 본문은 HTTP 요청에서 선택적 요소이므로 클라이언트가 HTTP 요청에 데이터를 추가하면 변경된다. 요청 매개변수는 클라이언트가 HTTP 요청에 첨부하여 서버에 특정 정보를 보낼 수 있는 키-값 쌍이다. 소량의 개별 데이터를 전송할 때 요청 매개변수를 사용하고, 더 많은 데이터를 교환해야 할 때 HTTP 요청 본문을 사용한다.
A.3.3 HTTP 응답: 서버가 응답하는 방식
앱에서 클라이언트 요청을 처리했다면 그다음은 서버 응답을 구현해야 한다. 클라이언트 요청에 대해 서버는 다음을 응답한다.
- 응답 상태(response status): 요청 결과의 간단한 표현을 정의하는 100에서 599 사이의 정수다.
- 응답 헤더(response header)(비필수): 요청 매개변수와 유사하게 키-값 쌍 데이터를 나타낸다. 클라이언트는 요청에 대한 응답으로 서버에서 클라이언트로 소량의 데이터(10~50자)를 보내는 데 사용된다.
- 응답 본문(response body)(비필수): 서버가 클라이언트로 대량의 데이터(예를 들어 몇백 개나 전체 파일)를 보낼 수 있는 방법이다.
다음 코드는 HTTP 응답을 시각적으로 이해할 수 있다.
HTTP/1.1 200 OK <- HTTP 응답은 HTTP 버전과 응답 코드 및 메시지를 지정한다.
Server: Microsoft-IIS/4.0 <- HTTP 응답은 응답 헤더로 데이터를 전송할 수 있다.
Date: Mon, 14 May 2012 13:13:33 GMT
Content-Type: text/html
Last-Modified: Mon, 14 May 2012 13:03:42 GMT
Content-Length: 112 <- HTTP 응답은 응답 헤더로 데이터를 전송할 수 있다.
<html> <- HTTP 응답은 응답 본문으로 데이터를 전송할 수 있다.
<head><title>HTTP Response</title></head>
<body><h1>hello Albert!</h1></body>
</html> <- HTTP 응답은 응답 본문으로 데이터를 전송할 수 있다.
- 상태 코드: 클라이언트 요청을 이해하고 모든 처리가 잘 되었는지, 아니면 클라이언트 요청 처리하는 동안 문제가 발생했는지 알려준다. HTTP 상태 코드는 요청의 전체 경과를 간략하게 나타낸 것이며, 서버가 요청의 비즈니스 로직을 처리할 수 있었는지 여부도 포함한다.
실제 구현에서 자주 마주치는 몇 가지 주요 상태 코드는 다음과 같다.
- 2로 시작하는 상태 코드는 서버가 요청을 올바르게 처리했다는 것을 의미한다. 요청 처리가 성공적이고 서버는 클라이언트 요청대로 작업을 수행했다.
- 200 OK: 이 코드는 가장 잘 알려져 있고 가장 간단한 응답 상태다. 서버가 요청을 처리하는 데 아무 문제도 없었다는 것을 클라이언트에 알린다.
- 201 Created: 예를 들어 이 코드는 POST 요청에 대한 응답으로 사용되며, 서버가 요청된 자원을 추가했다는 것을 성공적으로 클라이언트에 알린다.
- 204 No Content: 이 코드는 클라이언트가 이 응답에 대해 응답 본문을 기대하지 않아도 된다는 것을 알릴 수 있다.
- 4로 시작하는 상태 코드는 서버가 클라이언트 요청에 문제가 있었음을 알려주는 경우다.
- 400 Bad Request: HTTP 요청에 대한 문제를 나타내는 데 종종 사용되는 일반적인 상태다.
- 401 Unauthorized: 일반적으로 클라이언트에 요청 인증이 필요하다는 것을 알리는 데 사용되는 상태다.
- 403 Forbidden: 서버가 클라이언트에게 해당 요청을 실행할 권한이 없음을 알리고 일반적으로 보내는 상태 값이다.
- 404 Not Found: 요청된 자원이 없다는 것을 클라이언트에 알리기 위해 서버가 보내는 상태 값이다.
- 5로 시작하는 상태 코드는 서버가 서버 측에 문제가 발생했음을 통신하는 경우다. 이때 서버는 클라이언트가 요청을 완료하지 못했음을 알리는 상태 코드를 보내지만, 그 원인이 클라이언트의 잘못된 행동 때문이 아니라는 점을 알린다.
- 500 Internal Server Error: 백엔드가 클라이언트 요청을 처리하는 동안 문제가 발생했다는 것을 서버가 클라이언트에 알리고 보내는 일반적인 오류 응답이다.
A.3.4 HTTP 세션
- HTTP 세션(session): 서버가 동일한 클라이언트와 여러 번의 요청-응답에 상호 작용 사이에 데이터를 저장할 수 있는 메커니즘. HTTP에서 각 요청은 다른 요청과 서로 독립적이므로 한 요청은 이전 요청, 이후 요청, 동시에 오는 다른 요청에 대해 전혀 알지 못한다.
서버가 일부 요청을 서로 연관시켜야 하는 시나리오의 경우 다음과 같이 접근한다.
- 온라인 쇼핑몰의 장바구니 기능 - 사용자는 장바구니에 여러 품목을 추가한다. 품목을 장바구니에 추가하려면 클라이언트는 다른 요청을 해야 한다. 두 번째 품목을 추가하려면 클라이언트는 다른 요청을 해야 한다. 서버는 동일한 클라이언트가 이전에 추가한 동일한 장바구니에 품목을 추가했다는 것을 알아야 한다.
- 백엔드는 클라이언트에 '세션 ID'라는 고유 식별자를 할당한 후 이를 웹 메모리의 특정 위치와 연결한다. 세션 ID가 할당되면 클라이언트가 보내는 각 요청은 HTTP 헤더에 세션 ID를 포함해야 한다.
- 클라이언트가 더 이상 요청을 보내지 않으면, 일반적으로 세션은 특정 시간이 지나면 종료된다. 대부분의 웹 앱에서는 클라이언트가 더 이상 요청을 보내지 않으면 한 시간 미만으로 세션이 종료된다. 세션이 종료된 후 클라이언트가 다른 요청을 보내면 서버는 그 클라이언트를 위해 새로운 세션을 시작한다.

A.4 JSON 형식 사용
- JSON: 앱이 REST 엔드포인트를 통해 통신할 때 HTTP 요청 및 응답에서 교환하는 데이터 형식을 지정하는 데 자주 사용되는 방법
- REST 엔드포인트: 앱 간 통신을 설정하는 데 가장 많이 사용되는 방법
JSON으로 표현하는 것은 속성을 가진 객체의 인스턴스다. 속성은 이름으로 식별되고 값을 가진다.
예) Product라는 객체는 name과 price 속성을 가진다고 할 수 있다. Product 클래스의 인스턴스는 이 속성들에 값을 할당한다. name이 'chocolate'이고 price가 '5'라고 할 수 있다.
- JSON 표현 규칙
- 객체 인스턴스를 정의하기 위해 JSON에서는 중괄호({})를 사용한다.
- 중괄호 사이에 속성-값 쌍을 쉼표(,)로 구분하여 나열한다.
- 속성 이름은 쌍따옴표 사이에 작성한다.
- 문자열 값은 쌍따옴표 사이에 작성한다(문자열에 포함된 모든 쌍따옴표 앞에는 역슬래시(\)가 있어야 한다).
- 숫자 값은 따옴표 없이 작성한다.
- 속성 이름과 그 값을 콜론(:)으로 구분한다.
다음은 이름이 'chocolate'이고 가격이 '5'인 Product 인스턴스를 JSON 형식으로 표현한 이미지다.

객체는 한 속성 값으로 다른 객체 인스턴스를 포함할 수 있다. Product에 Pack이 포함되어 있고 Pack이 color 속성을 기술하는 객체일 때 Product 인스턴스의 표현은 다음 코드와 같다.
{
"name": "chocolate",
"price":5,
"pack": { <- pack 값은 객체 인스턴스다.
"color":"blue"
}
}
JSON으로 객체 컬렉션을 정의하려면 대괄호([])를 사용하고 쉼표로 항목을 구분한다. 다음 코드는 Product 인스턴스 두 개를 포함하는 컬렉션 정의 방법이다.
[ <- 대괄호를 사용하여 컬렉션의 객체 인스턴스를 둘러싼다.
{
"name":"chocolate",
"price":5
}, <- 인스턴스는 쉼표로 구분한다.
{
"name":"candy",
"price":3
}
]

이 글은 『스프링 교과서』 책을 학습한 내용을 정리한 것입니다.
'학습일지 > Java' 카테고리의 다른 글
| [스터디10] 02. JPA 시작 (0) | 2025.07.03 |
|---|---|
| [스터디10] 01. JPA 소개 (0) | 2025.06.28 |
| [스터디7] 08. REST 엔드포인트 사용 (1) | 2025.06.13 |
| [스터디8] 06. 열거 타입과 애너테이션 (3) | 2025.06.07 |
| [스터디7] 07. REST 엔드포인트 사용 (1) | 2025.06.04 |