[안드로이드 클라이언트] CH3 서버와 함께 Hello, world

1
post-thumbnail

[안드로이드 클라이언트] CH3 서버와 함께 Hello, world

이 포스팅은 <SNS 앱을 만들면서 배우는 안드로이드 클라이언트 개발>, 장성환, 비제이퍼블릭(2022)을 읽고 개인 학습용으로 정리한 글입니다.

3.1 오늘의 질문 개발 문서

3.2 HTTP

  • HTTP(Hyper Text Transfer Protocol): 인터넷 상에서 데이터를 교환하기 위한 규약

3.2.1 URI 구조

  • URI(Uniform Resource Identifier): 인터넷의 자원을 나타내는 유일한 주소
    -> URL(Uniform Resource Locator)은 URI의 하위 개념

URI의 구조

  • 스킴(scheme): 어떨게 자원을 가져올 지 알려줌(http, ftp, file)

  • 아이디, 비밀번호

  • 호스트(host): 서버의 연결을 구분하기 위해 사용하는 번호
    -> 프로토콜마다 표준값 있음(HTTP 80, HTTPS 443)
    -> 생략 가능

  • 경로(path): 서버에서 리소스의 위치 가리킴(대소문자 구분)

  • 질의(query): 서버에 추가로 전달되는 매개변수, 키-값 구조, &로 구분

  • 프래그먼트(fragment): 리소스 내의 위치

3.2.2 HTTP 요청

  • HTTP 요청은 세 부분으로 구성됨

  • 요청 줄(Request Line)

    • HTTP 메서드, 요청 타깃(Request Target), HTTP 버전 순서대로 나옴
    • HTTP 메서드: 서버에 이 요청을 어떻게 처리해야 하는지 알려줌
      -> ex. POST, GET, OPTIONS, ...
    • 요청 타깃: 요청이 전달될 위치
    • 버전: 버전에 따라 메세지의 형식이 다르기 때문에 이후의 메세지를 처리하기 위해 명시
  • 헤더(Headers)

    • 클라이언트나 요청에 대한 정보
    • 여러 개의 키-값으로 이루어짐, 콜론(:)으로 키와 값 구분
    • ex. User-Agent 헤더: 요청을 보내는 클라이언트에 대한 정보(OS, 브라우저, 앱 등의 종류나 버전)를 서버에 알려줌
    • 헤더까지 보낸 후 빈 줄을 추가하여 메타 정보 모두 보내졌음을 알림
      -> 그 아래 본문이 나옴
  • 본문(Body)

    • 요청과 함께 보낼 데이터(ex. 텍스트, 이미지, 동영상, ...)
    • 이 데이터를 헤더의 Content-Type에 따라 서버에서 처리

3.2.3 HTTP 응답

  • HTTP 응답도 요청과 마찬가지로 세 부분으로 구성

  • 상태 줄(Status Line)

    • 응답의 첫 번째 줄
    • HTTP 버전, 상태 코드(Status code), 상태 메세지(Status message)로 구성
  • 헤더

    • 서버와 응답에 대한 정보
    • ex. Server 헤더
    • 요청과 마찬가지로 헤더가 끝난 후 빈 줄 다음에 응답의 본문 나옴
  • 본문

    • 클라이언트로 전달 할 데이터
      -> ex. 요청한 사이트를 표시하기 위한 HTML 문서/이미지/파일

3.2.4 HTTP 메서드

  • GET

    • 서버의 리소스 조회, 서버의 리소스 변경 x
      ex. 파일 다운, 사용자 정보 조회

    • GET 요청은 쿼리를 통해 데이터 전달
      -> URI는 길이 제한이 있으므로 주의 필요

    • 일반적으로 GET 요청은 본문 X
      -> API에서 GET 방식의 본문을 필요로 한다면 사용할 라이브러리에서 이를 지원하는지 확인 필요

  • POST

    • 새로운 리소스 생성
      ex. 회원가입, SNS에 새로운 글 작성

    • 입력하는 폼(Form)이 있다면 일반적으로 POST로 요청
      -> 입력한 데이터를 HTTP 메세지의 본문으로 전송

  • PUT

    • 리소스를 생성하거나 리소스 전체를 교체

    • POST로 여러번 요청하는 경우: 새로운 리소스 계속 만들어짐
      PUT으로 여러번 요청하는 경우: 최초엔 새로운 리소스 만들어짐, 이후엔 교체됨

    • 보통 PUT 요청에는 리소스의 위치가 포함되어 있음

  • PATCH

    • 리소스의 일부 수정
      ex. 사진, 이름, 자기소개 등 다양한 필드로 구성된 사용자 정보 중 사진만 교체하는 경우
  • DELETE

    • 리소스 삭제
    • 본문의 유무 정의되지 않음
      -> 요청에 본문의 포함 여부 경우에 따라 다르다
  • 사용할 일이 적은 메서드들:

    • HEAD: GET과 동일하지만 응답에 본문 포함X
      -> 헤더로 보내주는 정보 필요할 때 사용

    • OPTIONS: 서버/리소스에 사용할 수 있는 메서드 조회

    • TRACE: 보낸 요청을 그대로 돌려줌

    • CONNECT: 리소스 연결 요청
      ex. 터널링에 사용

⭐멱등성과 안전한 메서드

  • 멱등성(Indempotent): 연산을 여러번 적용하더라도 결괏값이 달라지지 않는 성질
    ex. 특정 리소스에 대해 DELETE 요청을 한번 한 것과 여러번 한 것: 서버 입장에선 결과 같음(멱등성 성립)
    ex. 특정 리소스에 대해 POST 요청을 한번 한 것과 여러번 한 것: 매번 새로운 리소스 생성됨(멱등성 성립X)

  • 멱등성이 성립한다는 것 =/= HTTP 응답이 같다는 것

    • 응답을 기준으로 판단 X, 리소스 상태 기준으로 판단
    • ex. DELETE 요청을 처음 수행했을 때 성공 응답(204)를 받음
      그 후 DELETE 요청을 헸을 때 리소스가 없다는 응답(404)를 받음
  • 안전성(Safe): 리소스의 변경 여부로 판단
    ex. GET 요청은 아무리 해도 서버의 리소스 변화 X(안전성 성립)
    ex. DELETE 요청은 처음 요청시 서버의 리소스 삭제됨(안전성 성립X)

3.2.5 자주 사용하는 HTTP 헤더

  • HTTP 헤더
    • 요청 헤더: 클라이언트가 서버로 보내는 요청에만 사용
    • 응답 헤더: 클라이언트에게 서버의 응답이나 추가적인 정보를 제공
    • 일반 헤더: 응답과 요청 모두에 사용 가능, Entity와 관련 X
    • Entity 헤더: 요청과 응답의 Entity 처리

요청 헤더

  • Host: 요청하는 서버의 호스트 명, 포트
    (기본 포트(HTTP 80, HTTPS 443)을 사용하는 경우 생략 가능)

  • Accept: 클라이언트가 응답으로 받기를 원하는 컨텐츠 타입과 우선순위

  • User-Agent:요청하는 프로그램/프로그램이 실행 중인 환경에 대한 정보

  • Referer: 요청을 보낸 페이지의 주소
    ex. A 페이지에서 링크를 클릭해 B 페이지로 이동한 경우: B 페이지를 요청할 때 Referer 헤더로 A 전달

  • If-Modified-Since: 서버의 리소스가 특정 시간 이후 변경된 경우에만 Entity 보내달라 요청
    -> Entity 생략되었을 때: 클라이언트의 캐시 사용

  • ⚡If-None-Match: 서버 리소스의 Etag가 다른 경우 요청 처리
    ex. 요청에 포함되는 Etag과 서버의 Etag 일치하지 않는 경우 PUT 요청은 리소스 변경, GET 요청은 Entity를 포함한 응답 내려줌

응답 헤더

  • Server: 요청을 처리한 서버의 프로그램 정보

  • Location: 클라이언트가 요청한 리소스의 실제 위치
    ex. 클라이언트의 요청으로 리소스가 새로 생성됐을 때 클라이언트에게 이 주소를 알림
    ex. 리소스의 위치가 변경돼 클라이언트를 이동시키기 위해 사용

  • Etag: Entity 태그, 리소스의 특정 버전을 식별
    ex. 클라이언트는 응답으로 받은 Etag 값을 조건부 요청 헤더(If-Match, If-None-Match)로 전달해 사용

  • Last-Modified: 리소스의 마지막 수정 시간
    -> 클라이언트는 응답으로 받은 Last-Modified 값을 조건부 요청 헤더(If-Modified-Since, If-Unnmodified-Since)로 전달해 사용

일반 헤더

  • Date: HTTP 메세지가 만들어진 시간

  • Coonection: 네트워크 연결 재사용을 위해 유지 여부 제어
    -> close면 연결 끊기, keep-alive면 유지
    (HTTP/2에서는 항상 연결 유지 -> 사용 금지)

  • CacheControl: 서버와 클라이언트, 프록시 사이의 캐시 정책 제어
    -> 전송된 리소스가 유효하다고 판단되는 시간 max-age 지시자로 전달
    -> 응답 공유될 수 있는지 public, private 지시자로 전달

Entity 헤더

  • Content-Type: 본문의 콘텐츠 타입과 문자 인코딩 정보

  • Content-Length: 본문의 길이, 이 길이 정보를 통해 모든 데이터 받았는지 확인 가능
    -> 데이터 크거나 동적으로 생성해 Chunk로 나눠 보내는 경우: Tranfer-Encoding 헤더를 chunked로하고 Content-Type 헤더 생략

  • Content-Encoding: 본문 압축에 대한 정보
    -> 본문 압축하여 전송: 전송 속도와 대역폭에서는 이득, 서버와 클라이언트 성능에서는 손해

3.2.6 HTTP 상태 코드

  • HTTP 상태 코드: 5개의 카테고리로 분류, 100번대 ~ 500번대
    -> 번호대에 따라 상태 코드의 대략적인 의미 알 수 있음

1XX 정보

  • 100 Continue: 요청의 일부를 받았고 나머지를 이어서 올리길 기다립니다

  • 101 Switching Protocols: 클라이언트의 요청으로 프로토콜을 변경합니다
    -> Websocket을 사용할 때 많이 사용됨

2XX 성공

  • 200 OK: 요청 성공적으로 수행됨
    (의미 포괄적 -> 201, 202, 204등 구체적인 코드가 있는 경우에도 200을 내리는 경우 많음)

  • 201 Created: 요청이 성공적으로 수행되어 리소스가 만들어졌습니다

  • 202 Accepted: 서버가 요청을 접수했지만 아직 처리되지는 않았습니다

  • 204 No Content: 요청을 처리했지만 콘텐츠는 없습니다
    (ex. DELETE 요청을 수행한 결과, 콘텐츠 수정을 요청했지만 변경된 것이 없는 경우)

3XX 리디렉션

  • 303 Moved Permanently: 요청한 리소스가 응답의 Location 헤더의 위치로 옮겨졌습니다
    -> 리소스가 영구적으로 옮겨졌을 때 사용
    (ex. 사이트를 옮겼거나 여러 도메인을 사용할 때)

  • 302 Found: 요청한 리소스가 응답의 Location 헤더의 위치로 옮겨졌습니다
    -> 일시적으로 옮겨져 향후 원래 주소로 요청해야할 때 사용

  • 304 Not Modified: 요청한 리소스가 변경되지 않아 재전송을 할 필요가 없습니다
    -> 이미 클라이언트가 요청한 리소스를 가지고 있어, 데이터를 다시 받을 필요 없을 경우 사용

4XX 클라이언트 오류

  • 400 Bad Request: 클라이언트의 요청이 잘못됐습니다
    (ex. 요청에 포함되는 정보 생략되었을 때, 잘못된 형식일 때)

  • 401 Unauthorized: 인증이 필요한 리소스를 요청했지만 요청에 인증이 없습니다
    (ex. 로그인이 필요한 API를 호출했을 때)

  • 403 Forbidden: 권한이 없어서 요청이 거부되었습니다
    (ex. 관리자 권한이 필요한 기능을 일반 사용자가 호출한 경우, 본인만 변경할 수 있는 API를 다른 사람이 호출하는 경우)

  • 404 Not Found: 요청한 리소스가 없습니다, 존재하지 않는 리소스를 요청했습니다

  • 409 Conflict: 요청이 서버 상태와 충돌했습니다
    (ex. 이미 사용 중인 아이디로 가입하려는 경우, 친구 신청을 보냈는데 이미 친구인 경우)

5XX 서버 오류

  • 500 Internal Server Error: 서버에서 오류가 발생해 요청을 처리할 수 없습니다
    -> 서버의 스크립트, 데이터베이스 등 여러 구성 요소 중 하나에서 발생한 오류
    -> 오류에 대한 상세 정보를 본문으로 함께 전달하기도 함

  • 503 Service Unavailable: 서비스를 사용할 수 없습니다
    (ex. 서비스 점검 중, 너무 많은 요청으로 과부하 상태)

3.3 개발자 도구로 HTTP 요청과 응답 보기

  • 브라우저 F12 키 -> 개발자 도구 -> 네트워크 탭

3.4 오늘의 질문 레이아웃 만들기

  • 머티리얼 디자인은 8dp 그리드에 맞춰 구성 요소 배치 권장

  • Material로 시작하는 뷰: 머티리얼 디자인을 위한 기능들이 추가된 뷰

    • ex. MaterialCardView, MaterialTextView, MaterialButton
    • 호환성을 위해 androidx 패키지의 CardView, AppCompatTextView, AppCompatButton을 상속받음
  • LinearLayout의 showDividers

    • LinearLayout의 차일드 개수에 따라 자동으로 표시
  • TextAppearance

    • TextView의 경우
      -> 모든 뷰에서 사용할 수 있는 style 속성을 통해서는 위젯의 컬러나 크기 설정
      -> textAppearance 속성으로 텍스트의 크기, 폰트 패밀리 등 지정

    • 머티리얼 디자인에는 용도에 따라 사용할 수 있는 다양한 텍스트 스타일 준비됨
      ex. @style/TextAppearance.MaterialComponents.Headline6

3.5 HttpURLConnection으로 API 호출하기

  • 안드로이드 앱을 네트워크에 연결
    -> 매니페스트에 android.permission.INTERNET권한 추가

  • 안드로이드 9(API28)부터 보안을 강화하기 위해 암호화되지 않은 HTTP 프로토콜 사용 제한됨
    -> 매니페스트의 <<application>>에 android:usesCleartextTraffic="true" 추가

TodayFragment.kt


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        Thread{
            val url = URL("http://10.0.2.2:5000/v1/hello-world")
            val conn = url.openConnection() as HttpURLConnection
            conn.connectTimeout = 5000
            conn.readTimeout = 5000
            conn.requestMethod = "GET"

            conn.connect()

            val reader = BufferedReader(InputStreamReader(conn.inputStream))
            val body = reader.readText()
            reader.close()
            conn.disconnect()

            activity?.runOnUiThread {
                binding.question.text = body
            }
        }.start()
    }

📌참고자료

방법 1

  • View 사이사이에 seperator 역할을 하는 view들을 직접 삽입
<View
      android:layout_width="match_parent"
      android:layout_height="1px"
      android:layout_marginTop="5dp"
      android:layout_marginBottom="5dp"/>

방법 2

  • LinearLayout의 showDividers 속성을 true로 한다
  • API 11 이상에서만 가능
<LinearLayout
	android:layout_width="match_parent"
	android:layout_height="match_parent"
    android:divider="@drawable/separator"
    android:showDividers="middle"
    android:orientation="vertical">
		...
</LinearLayout>

방법 3

  • ButtonBarStyle 사용
<LinearLayout
	android:layout_width="match_parent"
	android:layout_height="match_parent"
    style="?android:buttonBarStyle"
    android:orientation="vertical">
		...
</LinearLayout>
profile
Be able to be vulnerable, in search of truth

2개의 댓글

comment-user-thumbnail
2023년 3월 23일

안녕하세요 저자입니다. 검색해보다가 우연히 발견했습니다. 책의 내용에 이상한 부분이 있으면 카페로 문의주세요! https://cafe.naver.com/androidclientdev

1개의 답글