얼마전 회사에서 후배 개발자의 이슈를 함께 고민하던 중 특이점을 발견했습니다.

몇년이 지난 과거 제가 진행했던 프로젝트에 대한 이슈였는데요

GET 방식의 API를 @RequestBody를 이용하여 요청을 받아내고 있었습니다.

애초 제가 해당 프로젝트를 할 때는 그 데이터를 받을 때는 QueryPram으로 받도록 설계했었습니다.

RequestBody로 변경된 이유를 묻자, 취약점 점검 시 해당 부분이 문제점으로 발견되어 고치게 되었다고 하네요...

이슈 처리 바빠서 그 후배에게는 아직 설명을 못해주었지만, GET API를 QueryPram을 RequestBody로 변경한 것은 적절치 못합니다. 오늘은 그 이야기에 대해 포스팅 해보려고 합니다.

 

과거 프로젝트를 진행할 때 당시 개발자들과의 고민은 "어떻게 해야 MSA 서비스에서 RestFul한 API를 제공할 수 있을지"에 대해서 였습니다. API의 기본 아키텍쳐에 대한 고민이었죠. 그 중 가장 컸던 부분은 필터 기능에 대한 것이었습니다.

간단한 예시를 들자면 게시판에서 여러가지 조건검색을 할 수 있는데 이 필터 조건들을 어떻게 API에게 전달할지에 대한 고민이었죠. 곧장 저희도 RequestBody를 떠올렸습니다. 예를 들면 이러한 JSON구조를 생각해 볼 수 있겠네요.

{
    "filterId": "subject", // 어떤 타입의 필터인지? 제목검색, 날짜검색 등등
    "filterTarget": "개발", // 어떤 대상으로 필터링할것인지? 내용, 날짜 등등
    "filterOrder": "like", // 대상에 대해 어떤 조건으로 필터링할것인지? 포함돼는 문자열, 특정 날짜 범위 등등
}

흠... 확인히 RequestBody로 하면 깔끔하게 보이긴하네요, 쓰고싶어지는 유혹(?)이 듭니다. 하지만 저희는 당시 RequestBody방식을 선택하지 않았습니다. 왜냐하면 이건 RestFul하지 않기 때문이죠. 왜일까요?

기본적으로 RequestBody는 GET에서 사용하길 권장하지 않아요 아래와 같은 이유 때문입니다.

 

  • HTTP 표준 준수: HTTP/1.1 표준(RFC 7231)에 따르면, GET 요청은 요청 본문을 포함하지 않아야 합니다. GET 요청의 목적은 리소스를 조회하는 것이며, 본문을 포함하는 것은 표준을 벗어난 사용입니다.
  • 캐싱 문제: GET 요청은 캐시될 수 있어야 합니다. 캐시는 요청 URL을 기준으로 동작하므로, 본문을 포함한 GET 요청은 캐시 시스템에서 올바르게 처리되지 않을 수 있습니다. 본문을 포함한 요청은 캐시 일관성을 유지하기 어렵게 만듭니다.
  • 안전성과 멱등성: GET 요청은 안전하고 멱등적이어야 합니다. 즉, GET 요청은 서버 상태를 변경하지 않아야 하고, 동일한 GET 요청을 여러 번 실행해도 결과가 동일해야 합니다. 본문을 포함하는 GET 요청은 이러한 특성을 깨뜨릴 수 있습니다.
  • 클라이언트와 서버의 지원 부족: 많은 HTTP 클라이언트 라이브러리 및 서버 프레임워크는 GET 요청의 본문을 지원하지 않거나 무시합니다. 예를 들어, 일부 브라우저는 GET 요청 본문을 보내지 않으며, 일부 서버는 본문을 처리하지 않습니다.
  • 표준 툴 및 라이브러리 호환성: 많은 개발 도구와 라이브러리는 GET 요청에 본문이 없다는 가정을 하고 설계되어 있습니다. 따라서, GET 요청에 본문을 포함하면 이러한 도구들과의 호환성 문제를 일으킬 수 있습니다.

또한 첨언을 더 해보자면, GET은 기본적으로 URL에 데이터가 포함되길 기대되며, URL에 요청 파라미터가 포함된 형태 + Body에도 포함된 형태는 일관적이지않으며 API의 복잡도를 오히려 더 늘어나게 된다고 판단했기 때문입니다. 예를들어 다음과 같은 API를 상상해보세요. 

URL
[GET] http://localhost:8080/api/board/100

Requst Body

{
    "filterId": "subject", // 어떤 타입의 필터인지? 제목검색, 날짜검색 등등
    "filterTarget": "개발", // 어떤 대상으로 필터링할것인지? 내용, 날짜 등등
    "filterOrder": "like", // 대상에 대해 어떤 조건으로 필터링할것인지? 포함돼는 문자열, 특정 날짜 범위 등등
}

 

100이라는 boardSeq 게시판에 대해 RequestBody로 검색하는 기능인데... pathParam도 있고, Body에도 조건이 포함되어 있어요. 이러한 구조는 API의 직관성을 해친다고 생각합니다.

그럼 저희는 어떻게 했을까요? 저희는 JSON데이터를 URL인코딩하여 QueryPram으로 처리하였습니다.

URL
[GET] http://localhost:8080/api/board/100?filter=%7B%22filterId%22%3A%22subject%22%2C%22filterTarget%22%3A%22%EA%B0%9C%EB%B0%9C%22%2C%22filterOrder%22%3A%22like%22%7D

Requst Body

없음

 

URL하나만으로 모든 것을 표현할 수 있습니다. 또한 이러한 특징은 사용자가 URL를 타인에게 공유하거나 응용하여 사용 할 때도 도움을 줄 수 있습니다. :) 훨씬 직관적이죠?

그렇다면 보안취약점에 해당 부분이 문제가 된다면 어떻게 해결해야 할까요? 만약 저였다면 문제로 지적된 파라미터에 대해 암호화 처리 등을 통해 해결하였을 것 같아요. ㅎㅎ 아니면 문제가 될 부분이 아닌데 탐지 되었을 가능성도 있으니 그 부분도 고려를 해서 예외처리를 하던가 해야겠지요? 그 후배가 틀렸다는 이야기를하는 것은 결코 아닙니다 :) 개발에는 좋은 방향성은 존재하지만 정답이 있다고 할 수 없기 때문입니다. 어쨌거나 보안취약점은 해결이 되었으니까요 ㅎㅎ 과거의 깊은 고민과 철학들이 담당자가 달라지며 조금씩 조금씩 사라져가는 것은 아주 조금 슬프긴합니다. ㅋㅋ 사람이 변하고 시간이 흐르면 과거의 고민들도 함께 사라져가는 것 같아요.

여러분들의 생각은 어떤가요? 글 읽어주셔서 감사합니다!

 

 

* 개요

 - Dynamic Adaptive Streaming over HTTP는 클라이언트에게 네트워크 상황에 맞추어 적합한 비디오 화질을 선택하여 서비스받는 기술을 의미 한다.

 - 유튜브를 사용할 때 네트워크 환경에 따라 영상화질을 변화 시킬 수 있는 것이 그 예시


* 기본 동작

 1. DASH 서버는 비디오를 다양한 비트 rate로 구분하여 인코딩을 한다.

 2. 인코딩 된 비디오 데이터들을 세그먼트(또는 chunk) 단위로 분할해 놓는다.

 3. 클라이언트는 자신의 네트워크 상황에 맞추어서 비트 rate 적용 알고리즘을 수행, 네트워크 가용 대역폭을 계산하고 이를 기반으로 비디오 세그먼트의 비트 rate를 계산한다.

 4. 계산된 rate를 토대로 서버로부터 이에 따른 비디오 서비스를 받는다.


- DASH는 MP4, MPEG-2 Trankport Stream을 지원, DRM을 명시하지 않았지만 ISO/IEC 23001-7: Common Encryption 표준에 명시된 모든 DRM 기술을 지원한다.


참조 : 

http://donghoson.tistory.com/48

https://www.html5rocks.com/ko/tutorials/eme/basics/


  클라우드 서비스는 데이터 저장소를 사용자의 로컬이 아닌 서버에 두고, 네트워크 통신을 활용해 서버의 데이터를 이용하는 것을 의미한다. 이러한 시스템이 데이터가 마치 구름처럼 떠있고 이를 필요시마다 활용하기에 "구름"을 은유적으로 표현하여 이러한 명칭을 쓰게 되었다. 클라우드는 그 활용 용도에 따라 크게  IaaS, PaaS, SaaS 등 3가지로 구분 지을 수 있다. 이에 대한 설명은 다음과 같다.


◐ IaaS (Infrastructure as a Service)

  IaaS는 PaaS, Saas의 기반이 되어주는 기술이다. 기본적인 클라우드의 형태를 갖추기 위해서는 IP, 네트워크, OS, 스토리지 등 다양한 구성요소가 요구된다. 이러한 체계를 모두 갖추고 실질적으로 데이터를 입출력 할 수 있는 기반이 되는 형태를 IaaS라 한다.


◐ PaaS (Platform as a Service)

  IaaS를 기반으로 개발자가 별도의 플랫폼을 설계하거나 제작 할 필요 없이, 서비스를 개발 할 수 있는 환경을 제공하는 클라우드 서비스다. 이러한 형태의 클라우드는 관련된 응용 프로그램을 개발 할 수 있는 API를 함께 제공하고 있다. 관련된 서비스는 네이버 API, 구글 API, 아마존의 EC2와 같은 서비스를 예로 들 수 있다.


◐ SaaS (Software as a Service)

  다양한 응용프로그램을 클라우드 형태로 서비스하는 것을 의미한다. 예를들어, 한글과컴퓨터의 넷피스 24, MS의 Office 365와 같은 서비스처럼, 클라우드 상에 업로드 된 데이터를 웹 기술을 통해 접근하여 편집, 저장 할 수 있다. 이러한 서비스의 경우 스토리지 용량에 대해 유연하게 대처 할 수 있을 뿐만 아니라, 백업과 같은 관리 및 내부적인 시스템 처리에 대해 사용자가 별도로 신경써야 할 부분이 없다.


http://draw.io


알고리즘에 대한 순서도, 시스템 설계, DB 스키마를 설계 내용을 시각적으로 표현해주는 웹사이트다.


조작하는 방법도 몇번만 다루어보면 간단하게 익힐 수 있다.


무엇보다도 별도로 설치 할 필요 없이 그냥 네트워크만 연결되어 있다면 어디서든 마음껏 만들 수 있다는 것이 큰 장점인 것 같다.

라이브러리와 프레임워크를 혼용하여 사용하는 경우가 많다.

이유는 아마 그 차이에 대해 명확하게 정의된 것이 없기 때문에 그런것이다.

자료를 찾아보니 굳이 명확히 구분을 짓고자 한다면 다음과 같은 특징들을 잡을 수가 있었다.


- 프레임워크는 틀, 골격의 의미로 라이브러리를 내포 할 수 있다.

- 내가 만든 소스코드에서 부르게 되면 라이브러리, 반대로 내가 만든 소스코드가 불려가 동작하게 된다면 프레임워크이다.

- 라이브러리는 연장/기능을 담은 집합, 프레임워크는 이런 연장들의 사용방식 및 API, 규칙, 절차에 대한 명세


풀어서 정리하자면, 라이브러리는 내가 그냥 가져다 사용 할 수 있는 모듈이며 수정이 용이하다.

그렇기 때문에 내가 라이브러리를 가져다 쓴다고 생각 할 수 있다.

프레임워크는 이미 정해진 명세, 규칙이기 때문에 수정이 어렵다.

소스코드를 짤때 이 규칙을 토대로 프레임워크를 사용하므로, 프레임워크가 명시한 대로 사용 할 수 있을 뿐이다.

그래서 내가 짠 소스코드는 프레임워크에 의존하여 동작하게 하게 된다.


http://imcreator.tistory.com/7

위 링크에서 이 질문에 대한 답을 명확히 이해 할 수 있었다.

모듈은 가장 상위에 위치하는 구현의 단위,

컴포넌트는 런타임 엔티티를 참조하는 단위라고 생각하면

금방 그 차이를 이해 할 수 있을거라고 생각된다.

따라서 모듈과 컴포넌트는 상위와 하위관계를 명확히 구분짓기 어렵고

서로 다른 개념으로 바라보아야 한다고 생각한다.

그렇기 때문에 모듈 1000개가 모여 하나의 컴포넌트가 될 수도있고,

컴포넌트 1000개가 모여서 하나의 모듈을 구성 지을 수도있다.


쉽게 설명해서, 모듈이란 실질적으로 구현이 된 단위를 의미한다.

자료구조, 알고리즘 등 이를 제공하는 인터페이스라고도 할 수 있을 것이다.


반면, 컴포넌트는 실제적으로 동작하고있는 엔티티로써

활동중인 독립적인 단위를 중점적으로 보고 있다.


상위에 있는 링크에서는 서버와 클라이언트의 예로 설명하고 있다.

1개의 서버에게 서비스를 제공받는 100개의 클라이언트가 존재한다고 가정하자.

위에 설명한 내용으로 모듈, 컴포넌트의 개수를 각각 세어보면

서버가 구현된 모듈 1개, 클라이언트가 구현된 모듈 1개이므로

이 시스템 인프라의 총 모듈 개수는 2개이다.

컴포넌트의 경우 실제 동작하고 있는 엔티티를 의미하므로 총 101개가 된다.

+ Recent posts