TIL - 『HTTP 완벽 가이드』 3장 HTTP 메시지

MinWoo Park·2021년 5월 12일
0

TIL

목록 보기
43/49
post-thumbnail

Today I Learned

매일 배운 것을 정리하며 기록합니다.
『HTTP 완벽 가이드』를 통해 공부했던 내용들을 복습하고 정리하려고 합니다.


I. HTTP: 웹의 기초

03. HTTP 메시지

메시지의 흐름

메시지는 원 서버 방향을 인바운드로 하여 송신

트랜잭션 방향을 표현하기 위해 HTTP는 인바운드와 아웃바운드라는 용어를 사용합니다.

메시지가 원 서버로 향하는 것은 인바운드로 이동하는 것이고, 모든 처리가 끝난 뒤에 메시지가 사용자 에이전트로 돌아오는 것은 아웃바운드로 이동하는 것입니다.


다운스트림(Downstream)

HTTP 메시지는 강물과 같이 흐릅니다.
요청, 응답에 관계없이 모든 메시지는 다운스트림으로 흐릅니다.

메시지 발송자는 수신자의 업스트림입니다.

클라이언트에서 서버로 요청을 보내면 클라이언트는 업스트림이므로 서버로 향합니다.

마찬가지로 서버에서 클라이언트로 응답을 보내면 서버가 업스트림이므로 클라이언트로 향합니다.


메시지의 각 부분

HTTP 메시지는 클라이언트로부터의 요청이나 서버로부터의 응답 중 하나를 포함합니다.

메시지는 시작줄, 헤더 블록, 본문 이렇게 세 부분으로 이루어집니다.

시작줄은 이것이 어떤 메시지인지 서술하며, 헤더 블록은 속성을, 본문은 데이터를 담고 있습니다.


메시지 문법

요청 메시지는 웹 서버에 어떤 동작을 요구합니다.
응답 메시지는 요청의 결과를 클라이언트에게 돌려줍니다.

요청과 응답 메시지 모두 기본적으로 구조는 같습니다.
시작줄에서만 문법이 다릅니다.

요청 메시지의 형식
<메서드> <요청 URL> <버전>
<헤더>
(개행)
<엔티티 본문>

응답 메시지의 형식
<버전> <상태 코드> <사유 구절>
<헤더>
(개행)
<엔티티 본문>

메서드는 클라이언트 측에서 서버가 리소스에 대해 수행해주길 바라는 동작입니다.

요청 URL은 리소스를 지칭하는 완전한 URL 혹은 URL의 경로 구성요소입니다.

완전한 URL이 아니더라도 클라이언트가 서버와 직접 대화하고 있고 경로 구성요소가 리소스를 가리키는 절대 경로이기만 하면 대체로 문제가 없습니다.

서버는 URL에서 생략된 호스트/포트가 자신을 가리키는 것으로 간주할 것이기 때문입니다.

버전은 HTTP의 버전을 말합니다.
형식은 다음과 같습니다.

HTTP/<메이저>,<마이너>

상태코드는 요청 중에 무엇이 일어났는지 설명하는 세 자리 숫자입니다.
각 코드의 첫 번째 자릿수는 상태의 일반적인 분류('성공', '에러'등)를 나타냅니다.

사유 구절(reason-phrase)는 숫자로 된 상태 코드의 의미를 사람이 이해할 수 있게 설명해주는 짧은 문구입니다.
상태코드가 같다면 사유 구절은 달라도 같은 상태코드의 의미로 처리되어야 합니다.

ex) '200 OK' 와 '200 NOT OK'는 동등하게 성공을 의미하는 것으로 처리되어야 함.

헤더들은 이름, 콜론(:), 선택적인 공백 값, 빈 줄(CRLF)이 순서대로 나타납니다.
특정 HTTP 버전은 어떤 특정 헤더가 포함 되어야만 유효한 것으로 간주하기도 합니다.

엔티티 본문은 임의 데이터 블록을 포함합니다.
모든 메시지가 본문을 가지는 것이 아니기 때문에 때때로 CRLF로 끝나게 됩니다.


시작줄

모든 HTTP 메시지는 시작줄로 시작합니다.

요청 메시지의 시작줄은 무엇을 해야 하는지 말해줍니다.

응답 메세지의 시작줄은 무슨 일이 일어났는지 말해줍니다.

요청 메시지의 시작줄(요청줄)

서버에서 어떤 동작이 일어나야 하는지 설명해주는 메서드와 그 동작에 대한 대상을 지칭하는 요청 URL, 클라이언트가 어떤 HTTP 버전으로 말하고 있는지 포함합니다.

이 모든 필드는 공백으로 구분됩니다.


응답 메시지의 시작줄(응답줄)

응답 메시지는 수행 결과에 대한 상태 정보와 결과 데이터를 클라이언트에게 돌려줍니다.
응답 메시지에서 쓰인 HTTP의 버전상태 코드, 사유 구절이 들어갑니다.

마찬가지로 이 모든 필드는 공백으로 구분됩니다.


메서드

MDN에서 자주 쓰이는 메서드 몇 개만 가져왔습니다.

  • GET
    GET 메서드는 특정 리소스의 표시를 요청합니다. GET을 사용하는 요청은 오직 데이터를 받기만 합니다.
  • HEAD
    HEAD 메서드는 GET 메서드의 요청과 동일한 응답을 요구하지만, 응답 본문을 포함하지 않습니다.
  • POST
    POST 메서드는 특정 리소스에 엔티티를 제출할 때 쓰입니다. 이는 종종 서버의 상태의 변화나 부작용을 일으킵니다.
  • PUT
    PUT 메서드는 목적 리소스 모든 현재 표시를 요청 payload로 바꿉니다.
  • DELETE
    DELETE 메서드는 특정 리소스를 삭제합니다.
  • OPTIONS
    OPTIONS 메서드는 목적 리소스의 통신을 설정하는 데 쓰입니다.
  • PATCH
    PATCH 메서드는 리소스의 부분만을 수정하는 데 쓰입니다.

HTTP는 쉽게 확장할 수 있도록 설계되었기 때문에, 다른 서버는 그들만의 메서드를 추가로 구현했을 수 있습니다.

이러한 추가 메서드는 HTTP 명세를 확장하는 것이기 때문에 확장 메서드라고 불립니다.


상태 코드

상태 코드는 클라이언트에게 무엇이 일어났는지 말해줍니다.

상태코드는 100단위로 종류를 구분합니다.

100~199: 정보

200~299: 성공

300~399: 리다이렉션

400~499: 클라이언트 에러

500~599: 서버 에러


버전 번호

버전 번호는 HTTP/x.y 형식으로 요청과 응답 메세지 양쪽 모두에 기술됩니다.

HTTP 애플리케이션들이 자신이 따르는 프로토콜의 버전을 상대방에게 말해주기 위한 수단이 됩니다.

HTTP 버전 1.1 애플리케이션과 대화하는 HTTP 버전 1.2 애플리케이션은 1.2 버전의 새로운 기능을 사용할 수 없다는 것을 알아야합니다.

버전 번호는 어떤 애플리케이션이 지원하는 가장 높은 HTTP 버전을 가르킵니다.

주의할 점은 HTTP/1.0 애플리케이션이 버전 번호가 HTTP/1.1로 된 응답을 받았을 때, 단지 응답을 보낸 애플리케이션이 HTTP/1.1까지 이해할 수 있음을 의미하는 것입니다.

요청 측에서는 HTTP/1.0까지 지원하기에 1.1버전을 사용한다는 것은 아닙니다.

그리고 버전 번호는 분수로 다루어지지 않습니다.

버전의 각 숫자는 각각 분리된 숫자로 다루어 집니다.

예로 HTTP/2.22는 HTTP/2.3보다 큽니다.

왜냐하면 22가 3보다 크기 때문입니다.


헤더

시작줄 다음으로는 헤더가 자리합니다.
HTTP 헤더 필드는 요청과 응답 메시지에 추가 정보를 더합니다.
기본적으로 이름/값 쌍의 목록입니다.

헤더의 문법은 이름, 쉼표, 공백(없어도 됨), 필드 값, CRLF가 순서대로 오는 것입니다.

흔히 쓰이는 헤더의 예는 다음과 같습니다.

Date: Tue, 3 Oct 1997 02:16:03 GMT (서버가 응답을 만들어 낸 시각)
Content-length: 15040 (15,040바이트의 데이터를 포함한 엔티티 본문)
Content-type: image/gif (엔티티 본문은 GIF 이미지)
Accept: image/gif, image/jpeg, text/html (클라이언트는 GIF, JPEG 이미지와 HTML을 받아들일 수 있음)


엔티티 본문

HTTP 메시지의 세 번째 부분은 선택적인 엔티티 본문입니다.
HTTP 메시지의 화물이라고 할 수 있습니다.
이미지, 비디오, HTML 문서, 소프트웨어 애플리케이션, 신용카드 트랜잭션, 전자우편 등 여러 종류의 디지털 데이터를 실어 나를 수 있습니다.


메서드

안전한 메서드(Safe Method)

HTTP는 안전한 메서드라 불리는 메서드의 집합을 정의합니다.
GETHEAD 메서드는 HTTP 요청의 결과로 서버에 어떤 작용도 없기에 안전하다고 할 수 있습니다.

그러나 안전한 메서드가 서버에 작용을 유발하지 않는다는 보장은 없습니다.(개발자에게 달렸기 때문입니다.)

안전한 메서드의 목적은, 서버에 어떤 영향을 줄 수 있는 안전하지 않는 메서드가 사용될 때 사용자들에게 그 사실을 알려주는 HTTP 애플리케이션을 만들 수 있도록 하는 것에 있습니다.


GET

GET는 가장 흔히 쓰이는 메서드입니다.
주로 서버에게 리소스를 달라고 요청하기 위해 쓰입니다.
HTTP/1.1은 서버가 GET을 구현할 것을 요구합니다.


HEAD 메서드는 정확히 GET처럼 행동하지만, 서버는 응답으로 헤더만 을 돌려줍니다.
엔티티 본문은 반환되지 않습니다.

HEAD를 사용하면 리소스를 가져오지 않고도 리소스에 대한 어느 정도의 정보를 알 수 있고, 상태 코드를 통해 개체가 존재하는지도 알 수 있습니다.
또한 리소스의 변경 유무도 검사할 수 있습니다.

헤더가 GET으로 얻는 것과 정확히 일치함을 보장해야 합니다.
그리고 HTTP/1.1 준수를 위해 HEAD 메서드가 반드시 구현되어 있어야 합니다.


PUT

PUT 메서드는 서버에 문서를 씁니다.
서버는 요청의 본문을 가지고 요청 URL의 이름대로 새 문서를 만들거나, 이미 URL이 존재한다면 본문을 사용해 교체합니다.

PATCH와 PUT 둘 다 수정을 위해 사용하는데 PUT은 전체 수정, 교체를 할 때 사용합니다.


POST

서버에 입력 데이터를 전송하기 위해 설계되었습니다.


TRACE

클라이언트에게 자신의 요청이 서버에 도달했을 때 어떻게 보이게 되는지 알려줍니다.
주로 진단을 위해 사용됩니다.


OPTIONS

서버에게 여러 가지 종류의 지원 범위에 대해 물어봅니다.
서버에게 특정 리소스에 대해 어떤 메서드가 지원되는지 물어볼 수 있습니다.


DELETE

서버에게 요청 URL로 지정한 리소스를 삭제할 것을 요청합니다.


확장 메서드

HTTP는 필요에 따라 확장해도 문제가 없도록 설계되어 있습니다.

만약 확장 메서드를 정의한다면, 대부분의 HTTP 어플리케이션이 이해할 수 없을 겁니다.

마찬가지로 저의 HTTP 애플리케이션이 이해할 수 없는 확장 메서드를 사용하는 애플리케이션과 마주칠 수도 있습니다.

이런 상황에서는 확장 메서드에 대해 관용적인 것이 좋다고 합니다.

종단 간(end-to-end) 행위를 망가뜨리지 않을 수 있다면, 알려지지 않은 메서드가 담긴 메시지를 다운스트림 서버로 전달하려고 시도하는 게 낫다고 합니다.

그렇지 않다면 프락시는 501 Not Implemented 상태 코드로 응답해야 합니다.

확장 메서드를 다룰 때는 "엄격하게 보내고 관대하게 받아들여라"라는 오랜 규칙을 따르는 것이 가장 좋다고 합니다.


상태 코드

HTTP 상태 코드는 크게 다섯 가지로 나뉩니다.
상태 코드의 각 분류에 대해 요약해 보겠습니다.

100~199: 정보성 상태 코드

100: Continue, 요청자는 요청을 계속해야 한다. 서버는 이 코드를 제공하여 요청의 첫 번째 부분을 받았으며 나머지를 기다리고 있음을 나타냄.

101: Switching Protocols, 요청자가 서버에 프로토콜 전환을 요청했으며 서버는 이를 승인하는 중임.

정보성 상태 코드는 HTTP/1.1에서 도입되었습니다.
100번 대에서는 100 Continue 상태 코드가 혼란스러울 수 있습니다.
100 Continue는 HTTP 클라이언트 애플리케이션이 서버에 엔티티 본문을 전송하기 전에 그 엔티티 본문을 서버가 받아들일 것인지 확인하려고 할 때, 확인 작업을 최적화하기 위한 의도로 도입되었습니다.


200~299: 성공 상태 코드

서버는 대응하는 성공을 의미하는 상태 코드의 배열을 갖고 있습니다.
각각 다른 종류의 요청에 대응합니다.

200(성공): 서버가 요청을 제대로 처리했다는 뜻이다. 이는 주로 서버가 요청한 페이지를 제공했다는 의미로 쓰인다.

201(작성됨): 성공적으로 요청되었으며 서버가 새 리소스를 작성했다.

202(허용됨): 서버가 요청을 접수했지만 아직 처리하지 않았다.

203(신뢰할 수 없는 정보): 서버가 요청을 성공적으로 처리했지만 다른 소스에서 수신된 정보를 제공하고 있다.
204(콘텐츠 없음): 서버가 요청을 성공적으로 처리했지만 콘텐츠를 제공하지 않는다.

205(콘텐츠 재설정): 서버가 요청을 성공적으로 처리했지만 콘텐츠를 표시하지 않는다. 204 응답과 달리 이 응답은 요청자가 문서 보기를 재설정할 것을 요구한다(예: 새 입력을 위한 양식 비우기).

206(일부 콘텐츠): 서버가 GET 요청의 일부만 성공적으로 처리했다.


300~399: 리다이렉션 상태 코드

리다이렉션 상태 코드는 클라이언트가 관심있어 하는 리소스에 대해 다른 위치를 사용하라고 말해주거나 그 리소스의 내용 대신 다른 대안 응답을 제공합니다.

만약 리소스가 옮겨졌다면, 클라이언트에게 리소스가 옮겨졌으며 어디서 찾을 수 있는지 알려주기 위해 리다이렉션 상태 코드와 (선택적으로) Location 헤더를 보낼 수 있습니다.

리다이렉션 상태 코드 중 몇몇은 리소스에 대한 애플리케이션의 로컬 복사본이 원래 서버와 비교했을 때 유효한지 확인하기 위해 사용됩니다.

클라이언트는 문서가 언제 이후 수정된 경우에만 문서를 가져오고 싶다면 'If-Modified-Since' 헤더를 전송합니다.

그 문서가 그 날짜 이후에 변한 것이 없다면, 서버는 컨텐츠 대신 '304 Not Modified'로 응답할 것입니다.
리다이렉트될 URL에 대한 링크를 보낼 때 설명을 포함시키는 것이 좋습니다.

302, 303, 307 상태 코드는 중복되는 부분이 있는데 이는 HTTP/1.0과 HTTP/1.1 애플리케이션이 이 상태 코드를 다루는 방식의 차이점에 기인합니다.

HTTP/1.0 클라이언트가 POST 요청을 보내고 302 응답을 받으면, 클라이언트는 Location 헤더에 들어있는 리다이렉트 URL을 GET 요청으로 따라갑니다.

그런데 HTTP/1.1은 303 상태 코드를 사용합니다.

이 혼란을 막기 위해 HTTP/1.1 클라이언트의 일시적인 리다이렉트를 위해 302 대신 307을 사용합니다.

결국 서버는 리다이렉트 응답에 들어갈 가장 적절한 상태 코드를 선택하기 위해 클라이언트의 HTTP 버전을 검사할 필요가 있습니다.

300(여러 선택항목): 서버가 요청에 따라 여러 조치를 선택할 수 있다. 서버가 사용자 에이전트에 따라 수행할 작업을 선택하거나, 요청자가 선택할 수 있는 작업 목록을 제공한다.

301(영구 이동): 요청한 페이지를 새 위치로 영구적으로 이동했다. GET 또는 HEAD 요청에 대한 응답으로 이 응답을 표시하면 요청자가 자동으로 새 위치로 전달된다.
302(임시 이동): 현재 서버가 다른 위치의 페이지로 요청에 응답하고 있지만 요청자는 향후 요청 시 원래 위치를 계속 사용해야 한다.

303(기타 위치 보기): 요청자가 다른 위치에 별도의 GET 요청을 하여 응답을 검색할 경우 서버는 이 코드를 표시한다. HEAD 요청 이외의 모든 요청을 다른 위치로 자동으로 전달한다.

304(수정되지 않음): 마지막 요청 이후 요청한 페이지는 수정되지 않았다. 서버가 이 응답을 표시하면 페이지의 콘텐츠를 표시하지 않는다. 요청자가 마지막으로 페이지를 요청한 후 페이지가 변경되지 않으면 이 응답(If-Modified-Since HTTP 헤더라고 함)을 표시하도록 서버를 구성해야 한다.

305(프록시 사용): 요청자는 프록시를 사용하여 요청한 페이지만 액세스할 수 있다. 서버가 이 응답을 표시하면 요청자가 사용할 프록시를 가리키는 것이기도 하다.

307(임시 리다이렉션): 현재 서버가 다른 위치의 페이지로 요청에 응답하고 있지만 요청자는 향후 요청 시 원래 위치를 계속 사용해야 한다.


400~499: 클라이언트 에러 상태 코드

때론 클라이언트가 서버가 다룰 수 없는 무엇인가를 보낼 수 있습니다.
잘못 구성된 요청 메시지 같은 것이 있을 수 있으며, 가장 흔한 것이 존재하지 않는 URL에 대한 요청입니다.

400 Bad Request
이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다.

401 Unauthorized
비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다.

402 Payment Required
이 응답 코드는 나중에 사용될 것을 대비해 예약되었습니다. 첫 목표로는 디지털 결제 시스템에 사용하기 위하여 만들어졌지만 지금 사용되고 있지는 않습니다.

403 Forbidden
클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다.

404 Not Found
서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다.

405 Method Not Allowed
요청한 메소드는 서버에서 알고 있지만, 제거되었고 사용할 수 없습니다. 예를 들어, 어떤 API에서 리소스를 삭제하는 것을 금지할 수 있습니다. 필수적인 메소드인 GET과 HEAD는 제거될 수 없으며 이 에러 코드를 리턴할 수 없습니다.


500~599: 서버 에러 상태 코드

이번에는 클라이언트가 올바른 요청을 보냈음에도 서버 자체에서 에러가 발생하는 경우입니다.

클라이언트가 서버의 제한에 걸린 것일 수도 있고 혹은 게이트웨이 리소스와 같은 서버의 보조 구성요소에서 발생한 에러일 수도 있습니다.

프락시는 클라이언트의 입장에서 서버와 대화를 시도할 때 자주 에러를 만나게 됩니다.

500 Internal Server Error
서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다.

501 Not Implemented
요청 방법은 서버에서 지원되지 않으므로 처리할 수 없습니다. 서버가 지원해야 하는 유일한 방법은 GET와 HEAD이다. 이 코드는 반환하면 안됩니다.

502 Bad Gateway
이 오류 응답은 서버가 요청을 처리하는 데 필요한 응답을 얻기 위해 게이트웨이로 작업하는 동안 잘못된 응답을 수신했음을 의미합니다.

503 Service Unavailable
서버가 요청을 처리할 준비가 되지 않았습니다. 일반적인 원인은 유지보수를 위해 작동이 중단되거나 과부하가 걸렸을 때 입니다. 이 응답과 함께 문제를 설명하는 사용자 친화적인 페이지가 전송되어야 한다는 점에 유의하십시오. 이 응답은 임시 조건에 사용되어야 하며, Retry-After: HTTP 헤더는 가능하면 서비스를 복구하기 전 예상 시간을 포함해야 합니다. 웹마스터는 또한 이러한 일시적인 조건 응답을 캐시하지 않아야 하므로 이 응답과 함께 전송되는 캐싱 관련 헤더에 대해서도 주의해야 합니다.

504 Gateway Timeout
이 오류 응답은 서버가 게이트웨이 역할을 하고 있으며 적시에 응답을 받을 수 없을 때 주어집니다.

505 HTTP Version Not Supported
요청에 사용된 HTTP 버전은 서버에서 지원되지 않습니다.


헤더

헤더에는 특정 종류의 메시지만 사용할 수 있는 헤더와, 더 일반 목적으로 사용할 수 있는 헤더, 그리고 응답과 요청 메시지 양쪽 모두에서 정보를 제공하는 헤더가 있습니다.

헤더는 크게 다섯가지로 분류됩니다.

일반 헤더(General Headers)

일반 헤더는 응답 메시지 모두에서 사용되지만 컨텐츠 자체에는 적용되지 않는 HTTP 헤더입니다.
클라이언트, 서버, 그리고 어딘가에 메시지를 보내는 다른 애플리케이션들을 위해 다양한 목적으로 사용됩니다.

가장 흔한 general 헤더는 Date, Cache-Control 및 Connection 입니다.


요청 헤더(Request Headers)

요청 헤더는 HTTP 요청에서 사용되지만 메시지의 컨텐츠와는 관련이 없는 HTTP 헤더입니다.

서버에게 클라이언트가 받고자 하는 데이터의 타입이 무엇인지와 같은 부가 정보를 제공합니다.

Accept, Accept-, If-와 같은 요청 헤더들은 조건부 요청 수행을 허용합니다.

Cookie, User-Agent (en-US), Referer와 같은 다른 것들은 컨텍스트를 정확히 나타내어 서버가 응답에 맞출 수 있게합니다.


응답 헤더(Response Headers)

응답 헤더는 HTTP 응답에서 사용될 수 있는 HTTP 헤더이며, 메시지의 컨텐츠와는 관련이 없습니다.
Age, Location (en-US) 또는 Server와 같은 응답 헤더는 더 상세한 응답의 컨텍스트를 제공하기위해 사용됩니다.


엔티티 헤더(Entity Headers)

엔티티 헤더는 메시지 바디의 컨텐츠를 나타내는 HTTP 헤더입니다.

엔티티 헤더는 HTTP 요청 및 응답 모두에서 사용됩니다. Content-Length, Content-Language, Content-Encoding과 같은 헤더는 엔티티 헤더입니다.

본문에 들어있는 데이터의 타입이 무엇인지 말해줍니다.


확장 헤더(Extension Headers)

애플리케이션 개발자들에 의해 만들어졌지만 아직 승인된 HTTP 명세에는 추가되지 않은 비표준 헤더입니다.
HTTP 프로그램은 확장 헤더들에 대해 설령 그 의미를 모른다 할지라도 용인하고 전달해야 할 필요가 있습니다.


Reference :

데이빗 고을리, 브라이언 토티, 마조리 세이어, 세일루 레디, 안슈 아가왈, 『HTTP 완벽 가이드』, 프로그래밍 인사이트 (2014), p49-84.

MDN - HTTP 요청 메서드
MDN HTTP 상태 코드
MDN - HTTP 헤더

profile
물음표를 느낌표로 바꾸는 순간을 사랑하는 개발자입니다.

0개의 댓글