728x90

서버 개발을 하면서 REST API를 많이 접해볼 수 있습니다.
오늘은 이 REST가 무엇이며 일반 API와 REST API는 어떤 차이점을 가지고 있는지 알아보도록 하겠습니다.

REST란?

우선 REST API에 대해 알려면 REST가 무엇인지부터 알아야 합니다.

REST의 FullName은 REpresentational State Transfer인데, 직역하자면 '대표 상태 전송'이라고 해석할 수 있습니다.

REST는 HTTP프로토콜을 사용하며 분산형 하이퍼미디어 시스템의 아키텍처 스타일입니다. 

더보기

분산 하이퍼미디어 시스템(distributed hypermedia systems)이란?

여러 대의 컴퓨터나 네트워크로 구성된 시스템에서 하이퍼 미디어를 관리하고 제공하는 시스템

 

하이퍼미디어는 텍스트, 그래픽, 오디오, 비디오 등 다양한 멀티미디어 자료를 연결하여 구성한 것을 의미합니다.

즉 시스템 간 정보를 주고받는 시스템을 구축할 때 적용하는 아키텍처 스타일이라고 쉽게 표현할 수 있습니다.

이는 프로토콜이나 표준이 아니고 네트워크를 통해 애플리케이션을 느슨하게 설계하기 위한 지침으로 굳이 안 지켜도 됩니다.

다만 REST스타일을 벗어나게 된다면 REST API가 일반 API가 될 뿐입니다.

 

REST 구성

REST는  HTTP를 사용한 아키텍처 스타일이다 보니 HTTP에서 제공하는 자원들을 활용합니다.

1. 자원 - HTTP URL

REST에서 URL은 어디로 갈 것인지 혹은 어떤 정보를 가지고 있는지 즉 정보의 자원을 명시합니다.

즉 URL에는 행위에 관련된 표현이 들어가는 것은 부적절하며, 명사가 들어가는 것이 좋습니다.

2. 행위 - HTTP Method

REST는 HTTP Method로 어떤 행위를 할 것인지를 표현합니다.

행위는 보통 5가지를 사용하며, 이 행위에 대한 것은 아래 표로 정리해 보았습니다.

Method Action
GET 리소스를 조회
POST 리소스 생성
PUT 리소스 수정
PATCH 리소스 부분 수정
DELETE 리소스 삭제

3. 표현 - HTTP Message Pay Load

통신에서 반환된 리소스를 반환하며 리소스는 JSON or XML or HTML 등의 타입을 가집니다.

이때 통신 상태에 대한 HTTP Status가 반환되는데 코드에 대한 의미는 다음과 같습니다.

Code Description
1XX 서버가 요청을 받았으니 클라이언트는 작업을 계속 진행할 것을 의미
2XX 요청을 완료하였음을 의미
3XX URL이 변경되었거나 다른 URL을 호출 할경우
4XX Client에서 서버를 호출할 때 잘못된 호출을 할 경우
5XX 서버에서 요청을 처리 혹은 서버 자체가 잘못 되었을 경우

REST의 제약 조건

REST를 적용하려면 몇 가지 조건이 지켜져야 RESTful하다 할 수 있는데 어떤 조건들이 있는지 알아보겠습니다.

1. Client-Server

클라이언트와 서버의 완전 분리입니다.

클라이언트와 서버를 분리하여 개발을 함으로써 서로 변경사항에 대해 영향을 주지 않기 위함입니다.

 

클라이언트는 URI정보를 통해 서버를 호출할 수 있어야 하며,

서버는 HTTP를 통해 들어온 요청에 대해 데이터를 전달할 수 있습니다.

2. Stateless

Stateless 즉 무상태는 서버에서 클라이언트의 상태에 대한 정보를 가지고 있지 않다는 의미입니다.

이는 Stateless의 반대인 Stateful을 보면 더욱 쉽게 이해할 수 있습니다.

 

Stateful은 서버가 클라이언트의 상태를 가지고 있다는 것을 의미하며, 이는 클라이언트와 서버의 통신은 연결되어 있어 작업을 할 때 서버에서 클라이언트 상태를 보고 작업을 하는 것입니다. 이럴 경우 통신 중이던 서버에 장애가 생길 경우  클라이언트는 그동안 서버에 저장된 상태정보를 유실하게 되어 처음부터 통신을 시작해야 하는 경우가 생깁니다.

 

Stateless는 서버가 클라이언트의 상태를 가지고 있지 않아 클라이언트가 서버를 호출할 때마다 필요한 상태 정보들을 같이 넘겨줘야 한다는 번거로움이 존재합니다. 하지만 위의 경우처럼 서버가 장애가 생겼을 때 같은 기능을 하는 다른 서버가 해당 작업을 이어받아 처리할 수 있게 할 수 있습니다. 즉 서버 확장에 대해 Stateful에 비해 자유롭다고 볼 수 있습니다.

3. Cache

서버에서 응답을 줄 때 해당 리소스의 캐싱 가능 여부와 응답 캐싱기간을 알려주는 정보가 포함되어있어야 하며, 캐싱할 수 있어야 합니다.

이는 클라이언트와 서버의 성능을 향상하는데 도움을 줍니다.

4. Uniform Interface

Uniform Interface는 REST API인가?를 결정하는 가장 중요한데, 이는 클라이언트와 서버의 통신이 일관되고 표준화된 방식이 되도록 유도하는 요소입니다.

 

Uniform Interface는 4가지로 구성되어 있습니다.

- Identification of resources

각 리소스들은 고유한 URI를 가져 식별되어야 합니다.

쉽게 말하면 API를 생성할 때 URI는 고유하게 식별되어 위치를 특정할 수 있어야 한다는 의미입니다.

 

- Manipulation of resources though represenations

이것을 이해하기 위해선 우리는 우선 Represenation에 대해 알아야합니다.

GET http://localhost:8080/helth
Accept:text/plain
Accept-Language:en-US;q=0.8,en;q=0.7

위와 같이 서버가 잘 작동하고 있는지 확인하는 API를 호출한다고 가정해보겠습니다.

그럼 우리가 전달받는 리소스는 "OK"라는 데이터를 받을 수 있을 것 입니다.

하지만 이는 잘못된 설명이였습니다.

 

그 이유는 "OK"라는 것은 리소스가 아닌 represenation data이기 때문입니다.

왜 리소스가 아니냐면 우리는 "OK"라는 데이터를 응답으로 받았지만 여기서 리소스는 "확인을 위한 의미를 담은 문서"이기 때문입니다.

즉 represenation data는 'text/plain' 타입의 영어로 된 데이터가 되는 것 입니다.

 

그리하여 클라이언트는 서버에 'text/palin'을 표현해주길 원하면 텍스트 데이터를, 'text/html'을 표현해주길 원하면 html로 된 데이터를 전달 받을 수 있는 것 입니다. ( 물론 여러 표현방법에 대한 API는 서버에 구현이 되어있어야 합니다 안할경우 406error!! )

 

그럼 Manipulation of resources though represenations는 무슨 의미인가?

요청하는 URI는 동일하지만 표현방법에 따라 응답할때 전달하는 represenation Data 가 둘이상 지원된다는 의미입니다.

즉 URI가 같더라도 원하는 Represenation에 따라 여러 데이터를 응답 받을 수 있다로 설명이 가능할 것 같습니다. 

 

- Self-descrive messages

각 응답과 요청은 스스로 설명이 가능해야 합니다.

이게 무슨 의미냐면, 응답받은 데이터들이 JSON으로 왔다고 가정해 보겠습니다.

{
    "name": "hong gil dong",
    "description": "This is a living thing."
}

위와 같은 리소스를 응답으로 받았다면, 우리는 저 name이 사람이름인지 동물이름인지 알 수 있는 방법이 없을 것입니다.

이럴 때 응답만을 보고 저게 사람이름인지 동물이름인지 알 수 있게 자체적으로 설명이 있어야 한다는 뜻입니다.

 

Spring REST Docs와 같은 프레임워크를 활용하여 응답 리소스에 Doc의 link를 실어 보내주는 방법으로 해결을 해야합니다.

 

- Hypermedia as the engine of application state ( HATEOAS )

클라이언트는 서버로부터 전송받는 응답으로 다음 가능한 행위를 결정할 수 있어야합니다.

이게 무슨 뜻인가!

이전에 우리는 이름과 설명을 응답으로 받았습니다.

여기에 설정을 붙여보겠습니다.

우리가 만약 사람들 관리 사이트에서 유저 상세 화면을 개발하고 있다고 가정을 해보겠습니다.

이때 우리는 기본적으로 해당 유저에 대한 정보들을 가져오게 될 것 입니다.

{
    "name": "hong gil dong",
    "age": 27,
    "phoneNumber": "010-0000-0000"
}

이제 우리는 저 정보들을 가지고 개발을 하게 될 것입니다.

하지만 우리가 페이지 내에서 다음 사람을 보고 싶다거나 할 때 어떤 행위를 해야할 지 위 응답에는 포함되어있지 않습니다.

 

{
    "name": "hong gil dong",
    "age": 27,
    "phoneNumber": "010-0000-0000",
    "links": [
    	{
        	"rel": "self",
            	"href": "http://localhost:8080/user/2"
        },
    	{
        	"rel": "befor",
            	"href": "http://localhost:8080/user/1"
        },
    	{
        	"rel": "after",
            	"href": "http://localhost:8080/user/3"
        }
    ]
}

위와 같이 Hateoas를 적용한다면 이전 유저를 조회하려면 어떤 행위를 해야하는지, 이후 유저를 조회하려면 어떻게 해야하는지에 대한 정보들이 응답에 포함되어있습니다.

 

이로인해 클라이언트는 다른 정보 없이 응답받은 정보들 속에서 다음 행위를 결정할 수 있게 되는 것 입니다.

5. Layered System

클라이언트나 서버 사이 중간계층들을 둘 수 있도록 확장성을 열어 놓은 것을 의미합니다.

미들웨어, 프록시, 게이트웨이, 캐시 서버, 로드 밸런서 등이 이에 포함되며, 이렇게 중간계층이 추가된다 하더라도 클라이언트와 서버의 상호 작용에는 영향이 없어야합니다.

 

6. Code-On-Demand ( Optional )

REST 조건이긴하지만 필수 제약 조건은 아닙니다.

클라이언트가 서버에 코드를 요청시 서버는 클라이언트에서 실행될 소프트웨어 코드를 보내는 것이라 할 수 있습니다.

Java applets와 javaScript등이 있습니다. 

REST API

위와 같이 REST의 조건들을 지켜 API를 개발하였을 경우 이를 REST API라고 하며 이러한 REST API들로 개발된 웹 서비스를 'RESTful 하다' 라고 표현할 수 있습니다.

 

여러가지 장단점이 있는데, 

 

REST가 표준이 존재하지 않고 아키텍처 스타일이다보니 발생하는 장단점이 크다고 생각합니다.

장점은 위에서 계속 나왔듯이 확장성과 자율성이 뛰어난 API를 구축할 수 있다로 볼 수 있고,

단점은 너무 자율성이 높고 조건들이 많다보니 지켜지기 쉽지 않다고 볼 수 있습니다.

 

마치며

개인적으로 1, 2, 3, 5번은 HTTP API를 개발하면서 크게 신경쓰지 않아도 지켜지는 조건들이라고 생각합니다.

다만 4번의 조건들이 신경쓸 부분들이 많은 만큼 만약 REST API를 만들고 싶다면 조건들을 세심히 따져보시기 바랍니다.

 

REST API는 표준이 정해져 있지 않지만 조건들이 존재하는 만큼 어느정도 커스텀은 괜찮다고 보지만, 조건이 맞지 않다면 그냥 API개발했구나 생각하시면 될 것 같습니다. ( 제가 그랬습니다 ㅎㅎ )  


 참조

https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm

https://blog.npcode.com/2017/04/03/rest%EC%9D%98-representation%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80/

728x90
728x90

JPA에 대하여

 

우리가 JPA를 알기 위해서는 사전 지식으로 ORM이 무엇인지를 알아야 합니다.

이에 이 글은 ORM부터 알아본 후 JPA에 대해 다뤄보도록 하겠습니다.

ORM이란?

ORM( Object Ralational Mapping )은 단어를 풀어 해석하면 '객체 관계형 연결'이 됩니다.

이 기술은 애플리케이션과 데이터베이스 연결 시 기존에는 SQL언어를 애플리케이션 서버에서 직접 작성하였지만, 이를 서버에서는 객체로 정의하여 행위에 대한 Action을 하면  정의된 객체를 해석하여 행위에 필요한 SQL문을 작성하여 데이터베이스로 전달하는 말 그대로의 Mapping역할을 합니다.

 

이러한 ORM은 기존 Mybatis와 같은 기술을 사용하던 것을 '객체 지향'적으로 사용하기 위해 나온 기술이라고 봐도 무방 할 것이라 생각하는데, 어떤 탄생 배경이 있는지 알아보도록 하겠습니다.

ORM 탄생 배경

우리가 개발할 때 객체 지향 언어를 사용하여 개발을 하고, 관계형 데이터베이스를 사용하게 되면 프로그램 내에서 데이터베이스와 연결하여 SQL문을 직접 작성을 해줘 데이터를 얻거나 작업을 할 수 있습니다.

 

이때 우리는 객체 지향 언어의 데이터 표현 방법관계형 데이터베이스의 데이터 표현 방법의 차이를 확인해 볼 필요가 있습니다.

 

객체 지향 언어은 데이터를 객체에 상호 연결된 그래프로 표현을 하고, 관계형 데이터베이스는 데이터를 표형식으로 표현을 하게 됩니다.

이렇게 표현 방식이 다른 두 개념을 한 프로그램에서 사용을 하려면 표형식 표현방법을 객체에 상호 연결된 그래프 방식으로 변경하는 작업을 하게 되는데, 이는 개발자에게 번거로움과 오류 발생의 원인을 제공하게 됩니다.

 

이리하여 관계형 데이터베이스와 연결하여 SQL문을 작성하는 부분을 추상화시켜버리게 되는데 이것이 ORM입니다.

ORM의 장단점

위 설명만 보면 ORM을 만능으로 볼 수도 있습니다.

개발자의 번거로움을 줄여주고!! 오류 발생의 원인을 없애 주다니!! 하고 말입니다.

하지만 대부분의 기술이 그렇듯 장단점이 존재합니다.

장점

  • 사용하기 편하다.  - 강점 -
  • 직접적인 SQL문 작성이 없는 만큼 신경써야하는 부분이 줄어든다.
  • 객체를 정의하여 사용하고 얼마든지 수정 및 확장이 쉬워 유지보수가 편하다.

단점

  • ORM만 사용하여서는 복잡한 작업( 조회 혹은 수정 등 )을 대응하기 힘들다.
  • 객체 지향적이라 객체로 관리하게 되었다고 설계를 신경써서 하지 않으면 어떤 프로그램보다 복잡하고 힘든 프로그램이 만들어진다.

데이터 베이스를 객체 지향적으로 접근할 수 있는 만큼 설계의 중요성은 이루 말할 수 없을 정도이며,

사용하다 보면 그냥 SQL사용해서 개발하고 싶다라는 생각이 들 수도 있습니다.

다만 적절히 비즈니스 로직을 나눠 분석하여 사용할 경우보다 쉽게 개발할 수 있는 기술인 것에는 틀림없다 생각합니다.


JPA이란?

JPA(Java Persistent API)는 말그대로 JAVA Application에서 '객체와 관계형 데이터베이스 간의 매핑을 위한 API' 인터페이스의 모음입니다.

이 모음에는 3가지 구현체가 존재하는데, Hibernate, EclipseLink, DataNucleus가 있습니다.

Hibernate

하이버네이트는 JDBC API를 사용하는 JPA인터페이스 구현체입니다.

이는 무슨 뜻이냐 우리가 객체로 데이터를 CRUD 하게 되면 JPA 즉 하이버네이트 내부에서는 해당 객체를 분석하여 데이터베이스로 SQL문을 보내주게 되는데 이때 사용하는 API가 JDBC API라는 것입니다.

EclipseLink

EclipseLink는 JPA인터페이스 구현체입니다.

또한 JAXB, SDO를 구현한 포괄적인 오픈소스 프레임워크로, 다양한 기능과 확장성을 강력하게 제공하고 있습니다.

DataNucleus

DataNucleus는 JPA 인터페이스 구현체입니다.

EclipseLink와 마찬가지로 다른 데이터베이스들 또한 지원하고 있습니다.

Hibernate를 왜 많이 사용하는가

글을 작성하면서 의문이 들었던 점이 왜 Hibernate는 많이 사용하거나 본 거 같은데, EclipseLink와 DataNucleus는 다루는 글을 많이 못 본 것 같다는 생각을 하였습니다. 그리고 설명들을 보면 Hibernate는 JPA 인터페이스를 구현한 가장 대표적인 오픈소스라고 하는데 EclipseLink와 DataNucleus 또한 강력한 기능과 확장 가능성을 지원해 준 것을 확인할 수 있었습니다.

 

우선 찾아보며 생각한 것은 Hibernate의 강력한 커뮤니티와 그로 인해 나온 많은 선례, 또한 만들어진 지 오래된 만큼 안정성을 보장하기 때문이라 생각합니다.

JPA주요 특성

ORM

엔티티 클래스와 데이터베이스 테이블 간의 매핑을 지원합니다.

영속성 컨텍스트

데이터를 영속성 컨텍스트에 저장하고 지속적으로 추적하게 되는데, 이 데이터가 수정될 경우 트랜젝션이 걸려있지 않으면 바로 반영이 됩니다. 이게 무슨 의미인가 만약 데이터를 조회하여 엔티티로 받아서 바로 엔티티 속성을 수정한다면 실제 SQL문이 바로 날아가 변경된다는 뜻입니다.

 

즉 commit을 안 했는데 commit이 되는 현상으로 이러한 상황을 방지하기 위해서는 Transaction처리를 해주거나, 엔티티를 사용하지 말고 데이터를 수정할 때 사용하는 객체를 따로 만들어 해당 객체로 데이터 값들만 복사하여 사용하는 것이 안전한 사용법인 것 같습니다.

캐싱

영속성 컨텍스트는 한번 조회된 데이터들은 가지고있는데, 만약 조회되는 데이터가 영속성 컨텍스트에 저장되어있는 데이터라면 데이터베이스를 조회하지 않고 해당 데이터를 반환해줍니다. 이는 1차 캐시의 개념이며, 만약 1차 캐시에 없을 경우 2차 캐시를 조회하게되고, 거기에도 없다면 데이터베이스를 조회하게 됩니다.

 

이때 말하는 1차 캐시는 Hibernate에서 제공하는 영속성 컨텍스트 캐시이고, 2차 캐시는 Session Factory 캐시입니다.


Reference

 

Understanding EclipseLink

This chapter describes how to set up your JPA applications to work with a non-relational data source. There are many types of non-relational data sources. These include document databases, key-value stores, and various other non-standard databases, such as

eclipse.dev

 

 

JPA Getting Started Guide (v5.2)

Developing applications is, in general, a complicated task, involving many components. Developing all of these components can be very time consuming. The Java Persistence API (JPA) was designed to alleviate some of this time spent, providing an API to allo

www.datanucleus.org

 

 

What is Object/Relational Mapping? - Hibernate ORM

Idiomatic persistence for Java and relational databases.

hibernate.org

 

캐싱 참조자료: https://www.baeldung.com/hibernate-second-level-cache

 

728x90
728x90

데이터 마이그레이션 후 갑자기 목록 조회가 안되는 현상이 발생했다.

log를 확인해보니

라는 에러가 발생했다.

???

누구세요

에러 로그를 읽어보면 조회를 하는데 조회할때 할당된 패킷은 4,194,304byte인데 11,594,289byte의 패킷이 조회되서 에러 띄웠다고 한다.

그럼 Mysql의 패킷 단위를 변경하면 되는 것이 아닐까??

찾아보니 max_allowed_paket이라는 설정 값을 변경해주면 된다고 한다.

그럼 max_allowd_paket은 뭔가??
말 그대로 데이터 베이스 서버에서 한번에 전송되는 최대 데이터 패킷 크기를 결정하는 설정이라고 한다.

즉 데이터를 조회 혹은 등록 할 때 주고 받을 수 있는 데이터의 양이라고 보면 편할 것 같다.

우선 현재 max_allowd_paket 이 얼마로 설정되어 있는지 확인해보자.

show variables where Variable_name = 'max_allowed_packet';

확인 후 본인이 늘리고 싶을 만큼 늘려보자 ( 주의 패킷이 주고받는 데이터 양을 의미하는데 생각없이 너무 늘려버리면 그것은 메모리 누수로 이어질 수 있을 거 같다. )

SET GLOBAL max_allowed_packet =12000000

늘리고 난 이후 Mysql의 설정을 적용해 주도록 하자

FLUSH PRIVILEGES

이후 확인을 해보면 패킷 최대 용량이 변경 된 것을 확인할 수 있을 것이다.

위의 방법은 command로 적용하는 방법이였고,
설정 파일을 수정하려면 mysql 설정파일 my.cnf또는 my.init에서

max_allowed_packet=16M

로 설정을 추가 혹은 변경해주면 될 것이다.

오늘 하루도 화이팅하자...야근 하이....

728x90

'Error' 카테고리의 다른 글

[Error] java.util.LinkedHashMap cannot be cast to Class  (0) 2024.04.16
728x90

목차

  1. 이유
  2. 사용방법
  3. 후기

이유

local에서 개발하고 테스트 하다보면
여러개의 데이터 베이스를 생성 혹은 처음 프로젝트를 시작할 때 좀 깔끔한 상태로 시작하고 싶다는 생각을 할 수 있다.

개인적으로 이런 생각을 가지게 된 데에는 포트 충돌이 가장 큰 원인이였다.

예를 들어 Mysql을 사용하다 MariaDB를 사용하게 되어 설치를 하려하는데 포트가 충돌 되는 현상이 발생하였다.

무슨일일까?

Mysql을 설치할 당시 기본 포트로 3306번을 할당해 주게 되는데 MariaDB또한 3306포트를 기본으로 할당해주어 포트 충돌이 생긴 것이다.

물론 새로 설치하는 MariaDB를 3307로 바꿔 설치하면 되지만,
필자 처럼 특정 이유 없이 여러 포트에 생기는 것을 별로 안좋아하는 사람들도 있을 수 있다.

그래서 그때 부터 고민을 하게 되었는데

어차피 여러 서비스를 image로 만들어 container에 띄우게 되면 container만 갈아 치우는 느낌으로 하나의 포트만 두고 사용할 때만 꺼내 쓸 수 있는 데이터 베이스가 되지 않을 까 였다.

해서 이번 글에서는 위의 해결 방법에 대해 다루려 한다.

사용방법

우선 Docker를 설치해야하는데 이는 다음 글을 참고 하기 바란다.

Docker 설치하는 방법

Docker를 설치하였으면

이제 본인이 사용할 Image를 다운 받으면 된다.

1. 본인이 사용할 image를 검색한다.

우리가 데이터 베이스 이미지를 사용하기 위해선 docker hub에서 지원해주는 image를 다운받아 사용해야하는데 그 첫번째로 사용할 image를 검색하는 것이다.

docker search ${검색할 이미지 명}
ex) docker search mysql

#### 2. 검색한 image를 다운받는다.
>```bash
docker pull ${다운받을 이미지 명}
ex) docker pull mysql

3. image를 container로 실행시킨다.

--name: 실행시킬 container의 이름을 지정
-- MYSQL_ROOT_PASSWORD : container를 실행할 때 mysql의 root계정의 비밀번호를 지정
-- 3306:3306 mysql을 port 3306으로 설정 하여 실행

docker run --name mysql-container -e MYSQL_ROOT_PASSWORD=<password> -d -p 3306:3306 mysql:latest

#### 4. Docker container 시작/중지/정지
>```bash
# Docker 컨테이너 중지
$ docker stop ${container명}
# Docker 컨테이너 시작
$ docker start ${container명}
# Docker 컨테이너 재시작
$ docker restart ${container명}

4. 실행중인 Docker container 접속

docker exec -it ${container명} bash

#### 5. Mysql 접속
>```bash
  mysql -u ${mysql계정아이디} -p ${mysql비밀번호}

위 과정을 거치면 정상적으로 사용할 수 있다.

후기

업무상 본인 노트북을 가지고 다니며 업무용, 공부용, 프로젝트용으로 한번에 사용하다 보니 여러 데이터 베이스를 사용하는 경우가 많이 생기게 된다.

이때 헷깔리지 않고 잘 관리를 하기 위해 고민하였고 사용하고 있는데 확실히 편하다.

개인적으로 여러개를 동시에 실행시키면 노트북이 버벅 거릴 수도 있는데 솔찍히 말하면 컴퓨터 성능이 좋아 아직 그정도의 불편함은 느끼지 못하였고,
솔찍히 편하고 좋다.

한줄평

굉장히 편한거 같고 사용하는 데이터 베이스에 맞춰 컨테이너만 갈아 끼워주면 되서 좋다!!

728x90

'Server' 카테고리의 다른 글

SSE란? - Server Sent Events  (2) 2024.04.18
REST API란?  (0) 2024.04.14
[Docker] Docker 설치하기  (0) 2024.04.08
[Docker] Docker란 무엇인가?  (2) 2024.04.08
NATS란?  (0) 2024.04.08
728x90


이번 글에서는 Docker시리즈의 기본 Docker를 설치하는 방법에 대해 설명해보려한다.

Docker가 무엇인지에 대해 알고 싶으면 아래 링크를 참고하기 바란다.

Docker란 무엇인가?

사실 Docker공식 사이트에서 확인하면 되지만 그래도 필요한 분이 계실 거 같아 복습할겸 글을 써본다.

우선 설치 환경은 이렇다

OS: Ubuntu 22.04

따로 Kubernetes와 같은 것들은 따로 설치한다는걸 제외한 가정이기 때문에 가볍게 시작하겠다.

  1. apt-get을 최신으로 업데이트를 해준다.
  2. apt-get update
  3. apt-get install ca-certificates curl gnupg
  4. install -m 0755 -d /etc/apt/keyrings
  5. curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
  6. sudo chmod a+r /etc/apt/keyrings/docker.gpg
  7. echo \
    "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
    "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
    sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  8. 위 설정을 토대로 Docker을 받기 위해 apt-get 을 업데이트 시켜준다.
  9. sudo apt-get update
  10. docker 관련 패키지를 다운받는다.
  11. sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  12. docker 실행 docker run 만 작성해도 정상적으로 작동한다.
  13. sudo docker run hello-world
  14. docker가 정상적으로 작동 중인지 확인한다.
  15. systemctl status docker

참고

docker 설치 - https://docs.docker.com/desktop/install/ubuntu/

728x90
728x90


서버 개발자라면 한 번쯤 들어 보았을 Docker 과연 Docker가 무엇인가에 대한 호기심으로 인해 글을 작성한다.

Docker란?

Docker는 애플리케이션을 개발, 제공 및 실행하기 위한 개방형 플랫폼이라고 할 수 있다.

이게 뭘까??

단순하게 애플리케이션을 개발, 제공 및 실행하기 위해서는 그냥 실행하면 되지 않을까란 생각을 할 수도 있다.

이를 이해하기 위해서는 앞서 Docker의 Container가 왜 나왔는지 역사를 알아볼 필요가 있다.

Container 탄생기

서비스를 실행하기 위해서는 하나의 서버 할당은 당연한 수순이라 바라볼 수 있다.

이때 가정을 하나 해보도록 하자.

서비스를 배포하기 위해 하나의 서버를 할당하였는데 서비스를 배포하고도 서버의 성능이 많이 남는다고 가정하도록 하겠다.

이때 해당 서버에 또 다른 서비스를 배포하고 싶은데 이때 같은 서버를 사용할 경우 기존 서비스의 설정과 추가할 서비스의 설정이 충돌이 날 경우
개발자는 두 서비스 중 어떤 서비스를 서버에 배포할 것인지 고민할 수밖에 없다.

이렇듯 하나의 서버에 여러 서비스를 할당하기 위해 탄생한 것이 "서버 가상화"다.

서버 가상화는 물리적 서버 호스트에서 여러 개의 서버 운영 체제를 게스트로 실행할 수 있는 소프트웨어 아키텍처다.


위와 같이 하나의 서버에 n 개의 VM을 설치하여 각각 OS를 할당하여 서비스 간의 연결을 끊어버리는 방법으로 나왔다.

하지만 위의 방식을 보면 뭔가 무겁다는 생각이 들지 않나?

그렇다 서비스를 Host OS에 영향이 가지 않게 나눠 Guest OS로 서비스를 실행하였지만 각각의 VM OS를 구동하기 위한 파일들을 가지게 됨으로써 Host 서버의 메모리를 훨씬 잡아먹는 것이다.

그러면서 우리는 고민을 하게 될 텐데
서버 메모리가 부족해지면 어떻게 해야 할까??

서버를 더 좋은 서버를 구매하여 메모리를 늘릴까??
하지만 위 방법을 사용할 경우 만약 서비스를 구동하는 메모리를 초과한다면 또 메모리 낭비로 이뤄지게 된다.

그러면 OS를 따로 띄우는 것이 아니라 하나의 OS 위에 배포되는 서비스의 환경을 분리하면 되지 않을까?

이렇게 탄생한게 "Container"다.

Container는 위와 같이 Guest OS를 가지고 있지 않은 것을 확인 할 수 있는데,
이는 Linux 자체 기능인 chroot, namespace, cgroup을 사용하여 프로세스 단위의 격리환경을 구축하기 때문이다.

Container에는 Application을 구동하는데 필요한 라이브러리 및 실행파일만 존재하여 이미지로 만들었을 때 가상머신에 비해 이미지 용량이 대폭 주는 효과를 누릴 수 있다.

그럼 Container는 무엇인가??

Container란?

앞서 Container에는 Application을 구동하는데 필요한 라이브러리 및 실행파일만 존재한다고 하였다.

그리고 Container는 프로세스 단위의 격리환경이다.

이처럼 Container는 격리된 환경을 제공하며 프로세스의 생명주기를 관리하고있다.

즉 독립된 프로세스 개발환경을 보장받을 수 있는 환경 그것에 바로 Container라고 볼 수 있다.


그럼 Container는 Docker인가??

답은 아니요 다.

Container는 Docker에서 관리하는 프로세스의 단위 일 뿐 Docker가 Container라고 하기엔 너무 많은 기능이 Docker에 포함되어있다.

이제부터는 그 기능들에 대해 알아보겠다.

Docker의 기능은 무엇이 있을까?

크게 4가지로 볼 수 있는데

  1. 컨테이너 관리
  2. 이미지 관리
  3. 볼륨 관리
  4. 네트워크 관리

등이 있다.

하지만 위는 엄청 간략하게 말한것이고 어떤 기능들을 제공해 주는지 확인해보자!

이 사진은 Docker에서 어떤 기능을 지원해주는 지 확인할 수 있다.

Dockerd

dockerd는 container를 지속적으로 관리하는 백그라운드 프로세스(docker daemon)이다.

Docker API - Docker Client의 명령어를 /var/run/docker.sock unix소켓을 통해 API를 호출하여 dockerd가 작업을 수행한 이후 Client에게 Response를 return.

Docker CLI - Docker Command Line Interface

Storage mgmt - Container가 사용하는 파일 및 이미지 데이터를 효과적으로 관리하며, 컨테이너의 생성, 실행, 중지 및 제거등과 관련된 모든 작업에 관여한다.

libnetwork - 컨테이너의 네트워크 설정과 관련된 작업을 처리.

SwarmKit - Docker의 오케스트레이션 도구 즉 여러대의 호스트에서 컨테이너를 관리하고 조절하는기능을 제공.

  1. 서비스 디스커버리와 로드 밸런싱
  2. 서비스 복제 및 스케일링
  3. 다양한 네트워킹 옵션
  4. 상태 관리와 롤링 업데이트
  5. 보안 및 권한 관리

BuildKit - Docker 이미지를 빌드할때 사용하며 기존 docker build보다 더 빠르고 더 많은 기능과 유연성을 제공한다.

Docker Content Trust - Docker이미지의 보안을 강화하는 기능을 제공한다.

Image mgmt - Storage mgmt가 Container를 관리하면 Iamge mgmt는 Image를 관리한다.
기본적으로 Image다운로드 및 업로드, 빌드, 생성, 버전관리, 검색, 삭제 등의 기능들을 제공한다.


마무리

이로써 기본적인 Docker에 관해 알아보았다.

결국 Docker이미지 처럼 여러 container들을 관리하는 고래가 Docker라고 보면 될거 같다.

비록 하나하나 깊게 다루지 못했지만 기본적인 기능으로만 보았을때 생각보다 제공해주는 기능이 많아 좀더 열심히 공부해야 할거 같다.

한줄평

기본 설정만 잘해두면 서비스들을 배포하고 관리하는데 편하다!!


참고
https://docs.docker.com/get-started/overview/
https://www.youtube.com/watch?v=IiNI6XAYtrs

728x90
728x90


프로젝트를 진행하다보면
"A의 값이 없을 때 B의 값을 보여주세요!!"를 볼 수 있습니다.

이떄 Typescript 를 사용해보신 분들이라면 아시겠지만 ||??를 봤을 것이다.

어떻게 사용하는지는 알텐데 정확히 어떤 점이 다른지가 궁금할 수 있는데,
이 글에서는 그 각각 어떤 효과를 내고, 어떤 의미를 가지는지를 알아보도록 하겠다.

Null병합 연산자( ?? )

개념

Nullish Coalescing Operator으로 불리며 피연산자가 null이거나 undefined라면 해당 피연산자의 대체 값을 사용하는 연산자다.

사용법

const text = null;

console.log(text ?? '값이 없습니다.');

// 출력값 = '값이 없습니다.'

특징

  1. 왼쪽에 있는 피연산자가 null이거나 undefined인 경우에만 false로 인식
  2. 논리연산자 OR(||) 연산자의 특수한 경우이다.
  3. 연산자들중 우선순위가 5번째로 낮으며, ||보다 한단계 낮은 우선순위를 가진다.
  4. AND(&&)와 OR(||)들과 직접 결합하여 사용 못한다.
  5. false로 인식될 수 있는 값들을 false로 처리하지 않는다. ( ex: 0, '' )

논리연산자 OR ( || )

개념

Logical OR으로 불리며 피연산자들 사이에 연결하여 사용하며,
앞의 피연산자가 false인 경우 뒤의 피연산자를 검사, 해당 피연산자가 true인 경우 해당 값을 사용.

boolean과 사용할 경우 boolean값을 반환하지만, 다른 자료형의 값을 사용할 경우 boolean값이 아닌 값을 반환한다.

사용방법

const text = '';

console.log(text || '값이 없습니다.');

// 출력값 = '값이 없습니다.'

특징

  1. 왼쪽에 있는 피연산자의 논리가 false인 경우 우측 피연산자를 출력
  2. false로 인식하는 값은 boolean, '', "", 0, undefined, Nan이 있다

후기

이 글을 작성하게 된 이유는 프로젝트 진행중 ??연산자를 사용한 부분에서 error가 발생하여 해당 문제를 해결하기위해 알아보는 와중 궁금해서 찾아보았다.
두개의 연산자가 서로 비슷한 출력을 한다 하더라도,
세세하게 보면 출력되는 값이 다르다는 것을 알 수 있다.

이로써 두 연산자를 분리하여 적재저소에 잘 사용하길 바란다.

일단 나부터...

728x90

'Front > TypeScript' 카테고리의 다른 글

[React] ImageUpload 여러 기능들  (0) 2024.04.08
728x90


Image를 업로드 한다 할 때 우리는 여러가지 기능들을 기대하게 됩니다.

미리보기 기능이 있으면 올린 파일이 어떻게 보이는지 미리 알 수 있어서 좋고, Drag Drop기능이 있으면 파일을 보다 편하게 업로드 할 수 있고, 이미지 복사하여 붙여넣기 기능이 있다면 더욱 편하게 업로드 할 수 있을 것입니다.

이렇게 Image혹은 파일을 업로드 하는 것을 우리는 일상속에서 많이 찾아볼 수 있습니다.

그리하여 이번에는 Image를 업로드 할 때 많이 접할 수 있는 미리보기, Drag Drop, Copy Paste기능을 정리하여 보겠습니다.


Preview


이미지 미리보기는 요즘 이미지 업로드라면 대부분이 가지고 있는 기능이라 생각합니다.

사실 이미지를 업로드 할때 미리보기를 구현하는 것은 매우 쉬운일입니다.

이미지를 업로드하게 된다면 이미지 파일을 업로드 할 것이고, 우리는 그 이미지를 그냥 화면에 뿌려주기만 하면 되는 것이기 때문입니다.

위와 같이 이미지를 업로드 할경우 우리는 event를 통해 file객체를 얻을 수 있습니다.

이때 file객체를 읽어 화면에 보여주기 위해서는 FileReader Object를 사용하였습니다.
이 FileReader는 비동기로 작동하는 File을 읽을 수 있도록 해주는 기능을 제공해줍니다.

FileReader는 file를 읽어들이게되면 읽을 수 있는 정보를 result에 담아주게 되는데 이를 저장하였다가 <img/> 태그의 src속성에 넣어주면 위 Gif처럼 이미지를 미리보여줄 수 있습니다.

// 파일을 읽어 result데이터를 뽑아내는 코드
const reader = new FileReader();
reader.readAsDataURL(image);
reader.onloadend = () => {
  if(reader.result && typeof reader.result === 'string') setImageReaderPath(reader.result);
}

Drag Drop


사용자가 편하게 이미지를 업로드 시킬 수 있게 도와주는 기능인 Drag Drop기능입니다.
이 기능은 우선 어떤 목적을 가지고 사용할 것인지가 중요할 것 같습니다.

드래그 할 수 있는 영역을 지정해 두고, 해당 필드에만 업로드 할 수 있게 하거나,
원하는 영역을 잡아 해당 영역을 드래그 드랍이 가능한 영역으로 잡는 것 입니다.

똑같은 것 아닌가??라고 생각하실 수 있지만,
코드 상으로 들어가게 되면 조금 달라진다는 것을 알 수 있습니다.

만약 드래그할 수 있는 영역을 따로 지정하여둔다면 코드 상에서 특별한 어려움 없이 해당 영역에만 이벤트 처리를 하면 됩니다.

다만 드래그를 할 수 있는 영역을 지정하지 않고, 전체영역과 같이 영역을 선택한다면, leave event를 조심해야할 것 입니다.
이유는 DragLeave이벤트는 본인 태그를 벗어나버릴 경우 호출되며, 이는 본인 태그의 자식태그로 마우스가 들어가게 되면 해당 이벤트가 발생하여 저와 같이 화면에 회색화면을 띄워주고 싶다 할 경우 문제가 생길 수 있기 때문입니다.

이를 해결하기 위해 저는 Drag영역과 DragLeave영역을 따로 설정하였습니다.

<div
  className={'image_upload_form'}
  onDragEnter={handleDragStart}
  onDrop={handleFileSelect}
  onDragOver={handleDragOver}
>
  {isDragging ? (
    <div
      className="fileDropZone"
      onDragLeave={handleDragLeave}
      >
      <div
        style={{display: "flex"}}
        >이미지를 업로드 해주세요.
      </div>
    </div>
  ) :
  selectImage ? <img src={selectImagePath} alt={'image'}/> : <span>파일 업로드</span>
  }

</div>

위와 같이 DragEnter즉 드래그를 인지하는 영역에 도달하면 isDragging을 활성화 하여 화면을 fileDropZone으로 덮어 드래그하는 도중 자식 필드를 만나 오류상황이 발생하는 경우를 최소화 하였습니다.

앞으로 해결해 나아가야할 부분이기도 하지만 좀더 깔끔하게 영역을 설정할 수 있도록 수정할 예정입니다.

또한 Drag Drop의 경우 웹 브라우저 상에 event가 걸려있어 해당 이벤트들을 중지 시켜주지 않으면 새 탭이 생기며 이미지가 띄워지는 상황이 발생하게 됩니다.

const handleFileSelect = (event: React.DragEvent<HTMLDivElement>) => {
  // console.log('작동?')
  event.preventDefault();
  event.stopPropagation();
  const file = event.dataTransfer.files.item(0);
  if(!file) {
    setIsDragging(false);
    return;
  }
  setSelectImage(file);
  const reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onloadend = () => {
    if (reader.result && typeof reader.result === 'string') setSelectImagePath(reader.result);
  }
  setIsDragging(false);
};

위 코드 중에서 가장 중요한 부분이 2개 있는데, 이는 event에 설정되어있는 고유 동작을 실행하지 않는 설정인 event.preventDefault()event.stopPropagation()입니다.

event.preventDefatul()는 고유적으로 설정되어있는 동작들을 실행하지 않겠다라는 의미를 가지고 있습니다.

고유기능이라함은 a태그로 보았을 때 a태그를 클릭하였을 때 페이지 이동이 되는 고유 기능이 존재하지만 event.preventDefatul()를 사용하여 해당 고유기능을 사용하지 않게 되어 페이지 이동이 되지 않게 하는 것 입니다.

event.stopPropagation()는 클릭 이벤트가 발생시 상위 엘리먼트들에게로 이벤트가 전파되는 것을 방지하는 것 입니다.

쉽게 지금일어나는 일은 여기서만 해결할께요로 봐주면 좋을 것 같습니다.


Copy Peste


복사 붙여넣기 기능입니다.
우리가 채팅에 많이 사용하며, 메일을 보낼때도 많이 사용해 보셨을 기능입니다.

우리가 ctrl + c를 하게되면 클립보드라는 곳에 우리가 복사한 내용이 임시 저장되게 되는데,
이때 우리가 복사한 이미지는 이 클립보드의 item으로 저장이되게 되는 것입니다.

const handlePaste = async (event: React.ClipboardEvent<HTMLInputElement>) => {
        const items = event.clipboardData.items;
        for (let i = 0; i < items.length; i++) {
            const item = items[i];
            if (item.type.indexOf('image') !== -1) {
                const blob = item.getAsFile();
                if(!blob) return;
                const file = new File([blob], blob.name, { type: blob.type });
                setSelectImage(file);
                const reader = new FileReader();
                reader.readAsDataURL(file);
                reader.onloadend = () => {
                    if(reader.result && typeof reader.result === 'string') setSelectImagePath(reader.result);
                }
            }
        }
    }

위와 같이 clipboardData의 item들을 꺼내와 그중 image type인 데이터들만 뽑아내게 됩니다.
이때 저는 file형태로 관리를 하고 있어 file형태로 변형하여 state관리를 하였고, 미리보기 기능을 위해 FileReader를 사용하여 미리보기 데이터를 관리하였습니다.


코드: https://github.com/KrongDev/imageUploadUI

728x90

'Front > TypeScript' 카테고리의 다른 글

[Typescript] Null을 대체하는 값  (0) 2024.04.08

+ Recent posts