daniel7481의 개발일지

[웹 프로그래밍(풀스택)] WEB API - BE 본문

Naver Boostcourse

[웹 프로그래밍(풀스택)] WEB API - BE

daniel7481 2022. 2. 23. 13:53
반응형

1. Rest API란?

API(Application Programming Interface)

- 응용 프로그램에서 사용할 수 있도록, 운영 체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스(출처: wiki)

- 주로 파일 제어, 창 제어, 화상 처리, 문자 제어 등을 위한 인터페이스를 제공함

- ex: 자바에서 제공하는 인터페이스 중 절대 값을 구하는 메소드 abs()를 살펴보면, 해당 메소드가 어떻게 구현되어있는지 알 필요 없이 인터페이스만 알면 사용할 수 있다. 이렇듯 프로그래밍 할 때 필요한 인터페이스를 API라고 한다.

REST API(REpresentational State Transfer)

- 2000년도에 로이 필딩의 박사학위 논문에서 최초 소개되었다.

- 말 그대로 REST형식의 API를 말한다.

- 핵심 컨텐츠/기능을 외부 사이트에서 활용할 수 있도록 제공되는 인터페이스

- ex: 네이버에서 블로그에 글 작성, 우체국에서 우편번호를 조회하는 기능을 제공, 구글에서 지도를 사용 할 수 있도록 제공하는 인터페이스

웹 브라우저 뿐만 아니라 다양한 클라이언트가 등장함으로써 그러한 클라이언트에게 대응하기 위해 REST API가 널리 사용되기 시작하였다. 서비스 업체들이 다양한 REST API를 제공함으로써 클라이언트는 이러한 REST API를 조합한 어플리케이션을 만들 수 있게 되었다. 이를 Mashup이라고 한다.

REST는 다음과 같은 스타일을 반드시 지켜야 한다.

  • client-server
  • stateless
  • cache
  • uniform interface
  • layered system
  • code-on-demand (optional)

uniform interface를 제외한 모든 특성은 HTTP 프로토콜을 사용하면 쉽게 구현이 가능하다. uniform interface는 다음과 같은 스타일(제약조건의 집합)이 있다.

  • 리소스가 URI로 식별되야 합니다.
  • 리소스를 생성,수정,추가하고자 할 때 HTTP메시지에 표현을 해서 전송해야 합니다.
  • 메시지는 스스로 설명할 수 있어야 합니다. (Self-descriptive message)
  • 애플리케이션의 상태는 Hyperlink를 이용해 전이되야 합니다.(HATEOAS)

첫 번째와 두 번째 항목은 지키기 어렵지 않은데, 메세지가 스스로 설명할 수 있다는 점과 HATEOAS를 지원하는 것은 웹과 다르게 API로는 쉽지 않다고 한다. 응답 결과에 JSON 메세지를 사용하게 되는데, 이 메세지가 어디에 전달되는지 그리고 메세지를 구성하는 것이 어떤 의미를 표현해야만 메세지가 스스로 설명할 수 있다고 하는데, 이는 쉽지 않다. 또한 우리가 웹 게시판을 사용할 때 리스트 보기를 보면, 상세보기나 글쓰기로 이동하는 링크가 있고, 상세보기에는 글 수정/삭제로 갈 수 있는 링크가 있다. 웹 페이지에서는 웹 페이지 자체에 관련된 링크가 있는데, 이를 HATEOAS라고 한다. 이러한 HATEOAS를 API에서 제공하는 것은 쉽지 않다.

REST API는 쉽지 않으므로 보통 Web API(HTTP API)를 사용한다.

기억해야할 점

- REST의 모든 것을 제공하지 않으면서 REST API라고 말하는 경우도 있다.

- REST의 모든 것을 제공하지 않고 Web API(혹은 HTTP API)라고 부르는 경우가 있다.

2. Web API란?

Web API에는 중요한 원칙이 있다.

1. URI는 정보의 자원을 표현해야 한다.

2. 자원에 대한 행위는 HTTP Method(Get, Post, Put, Delete)로 표현한다. 이러한 메소드에 따라서 API에 요청하는 바가 달라진다.

URI는 정보의 자원을 표현해야 한다.

  • GET /members
    : 위의 표현은 맴버의 모든 정보를 달라는 요청입니다.
  • GET /members/delete/1
    : GET은 정보를 요청할 때 사용합니다. 위와 같이 동사로 삭제를 표현하면 안 됩니다.
  • DELETE /members/1
    : HTTP Method 중의 하나인 DELETE를 이용하여 삭제를 표현해야 합니다.

자원에 대한 행위는 HTTP Method로 표현해야 한다.

  • GET /members/1                   (o)
  • GET /members/get/1              (x)
  • GET /members/add                (x)
  • POST /members                    (o)
  • GET /members/update/1         (x)
  • PUT /members/1                   (o)
  • GET /members/del/1              (x)
  • DELETE /members/1               (o)

조회/입력/삭제/수정과 관련된 것들이 동사로 표현되면 안된다. 이러한 동사를 표현하려면 앞에 있는 HTTP 메소드로 표현해야 한다.

슬래시 구분자(/)는 계층을 나타낼 때 사용한다.

ex: http://domain/houses/apartments(모든 집 중에 아파트만 주세요)

    http://domain/departments/1/employees(모든 부서 중에서 1번 부서, 1번 부서 중에서 사원에 대한 정보를 주세요)

- URI 마지막 문자로 슬래시 구분자를 포함하지 않는다.

- 하이픈(-)은 URI 가독성을 높일 때 사용하고, 언더바(_)는 사용하지 않는다.

- URI 경로는 소문자만 사용한다. RFC 3986(URI 문법 형식)은 URI스키마와 호스트를 제외하고는 대소문자를 구별한다.

- 파일 확장자는 URI에 포함시키지 않고 Accept Header를 사용한다.

200번은 대부분은 성공을 의미한다.

400번대는 클라이언트에서 문제가 발생할 때 나오는 상태코드이다.

500번은 대부분 서버가 잘못됬을 때 나오는 상태코드이다.

3. Web API 실습-1

먼저 Maven Project를 하나 만들어주자. 이번엔 Architect를 or.apache.maven.webapp으로 만들어주자. Groupid는 kr.or.connect, artifactid는 webapiexam으로 만들어주자. 이번에도 pom.xml에 몇 가지 설정을 해줘야 하는데, 앞에서 sql을 사용하기 위해 했던 것처럼 mysql-connector 라이브러리를 가져와야 하고, json을 사용하기 위해 jackson-databind, 서블릿을 사용하기 위해 javax.servlet, JSTL을 사용하기 위해 jstl 라이브러리를 가져오자.

	<dependency>
    <groupId>mysql</groupId>
    	<artifactId>mysql-connector-java</artifactId>
    	<version>8.0.28</version>
		</dependency>
        <dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.9.4</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

다음 Window-show view-Navigator를 탭한 후, 왼쪽에 Navigator 칸에서 webapiexam-settings-org.eclipse.wst.common.project에서 jst.web version을 2.3에서 3.1로 바꿔줘야 한다.(서블릿이 2.5버전부터 사용되기 때문). 이제 이클립스를 재시작해야 한다. 이제 webapiexam의 properties에 들어간 후 Project Facets를 들어가면 Dynamic Web Module의 버전이 3.1로 잘 바뀐 것을 알 수 있다.

우리는 Annotation을 이용해서 서블릿을 설정할 것이기 때문에 web.xml이 필요 없으니 삭제해주자. pom.xml에서 web.xml을 찾으려고 할 것이기 때문에 properties에 다음과 같은 태그를 넣어주자

<failOnMissingWebXml>false</failOnMissingWebXml>

이제 src/main에 java라는 폴더(클래스, 라이브러리를 저장할 디렉토리)를 만들어주자. 이제 src/main/java에 패키지를 하나 생성할건데 이름을 kr.or.connect.webapi.api라고 해주자. 이제 지난 번에 만들었던 jdbcexam.dto, .dao를 복붙해주자. 이러한 복붙 작업은 반드시 이클립스 위에서 해야 한다. 파일 탐색기에서 하면 안된다.

먼저 role 테이블의 모든 정보를 가져오는 getRoles를 사용해보자. RoleServlet.java 서블릿을 만들어보자.

protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setCharacterEncoding("utf-8");
		response.setContentType("application/json");

		RoleDao dao = new RoleDao();

		List<Role> list = dao.getRoles();

		ObjectMapper objectMapper = new ObjectMapper();
		String json = objectMapper.writeValueAsString(list);

		PrintWriter out = response.getWriter();
		out.println(json);
		out.close();
	}

encoding은 utf-8로 만들고, 우리가 받을 Type을 원래는 text/html로 항상 해왔지만 이번에는 json으로 받을거기 때문에 application/json으로 써주자. 이제 RoleDao로 객체를 선언해주고, 받아올 객체 list를 선언해주자. 다음 objectMapper 객체는 json 라이브러리에서 JSON 문자열로 바꾸거나 JSON 문자열을 객체로 바꿔주는 역활을 수행해준다. writeValueAsString은 파라매터로 리스트를 넣어주면 해당 리스트가 JSON 문자로 바뀌어서 리턴해주는 메소드이다. PrintWriter 객체로 JSON 문자열을 출력해주면 된다. 이제 실행을 하면 다음과 같은 결과를 볼 수 있다.

[{"roleId":102,"description":"Project manager"},{"roleId":101,"description":"Researcher"},{"roleId":100,"description":"Developer"}]

JSON은 javaScript Objet Notation의 약자로 메세지를 교환하는 형식이다. 대괄호는 배열/리스트를 의미하고, 중괄호는 객체 한 건을 의미한다. 그 안에는 작은 따옴표 혹은 큰따옴표로 묶이는 속성명이 나오고, 콜론 다음에는 값이 나오게 된다. 

간혹 라이브러리를 제대로 불러왔는데 import되지 않는다던지 이러한 문제가 발생한다. 이럴 땐 C드라이브의 User에 들어가면.m2라는 디렉토리가 보이는데, 이 안에 repository등 우리가 사용하는 라이브러리가 있다. 만약 제대로 저장이 되있는데 읽어들이지 못한다면 이클립스를 닫고 .m2 폴더를 삭제해준다. 다시 이클립스를 실행하고 Update Project하면 잘 될 수 있을 것이다.

4. Web API 실습-2

이번에는 아이디 한 건에 대해서 role 정보를 읽어오는 서블릿을 작성해보자. 이번에 서블릿 이름은 RolesByIdServlet으로 하되, url mapping을 /roles/*로 해주자. 이렇게 *를 사용하면 path가 roles로 시작하고 뒤에 어떤 문자든지 올 수 있다라고 생각하면 된다. 

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setCharacterEncoding("utf-8");
		response.setContentType("application/json");
		
		String pathInfo = request.getPathInfo();
		String[] pathParts = pathInfo.split("/");
		String idStr = pathParts[1];
		int id = Integer.parseInt(idStr);
		RoleDao dao = new RoleDao();
		Role role = dao.getRole(id);
		ObjectMapper objectMapper = new  ObjectMapper();
		String json = objectMapper.writeValueAsString(role);
		PrintWriter out = response.getWriter();
		out.println(json);
		out.close();
	}

request.getPathInfo로 url의 주소값을 받아오고, /를 기준으로 나눠준다. 우리는 /roles./{role_id}의 형식을 가질 것이기 때문에 1번째 인덱스가 id가 되겠다. Integer.partInt로 받아온 문자열을 숫자로 바꿔주고, 그 밑에 과정은 위와 같다. 이번에 dao.getRole의 파라미터로 우리가 url에서 받아온 id를 넣어주기만 하면 된다. 이제 실행하면 오류가 발생하게 되는데, 이는 우리가 설정한 url mapping이 *로 되어있기 때문이다. *을 존재하는 숫자로 바꿔서 넣어주면 그에 상응하는 role이 나오게 될 것이다.

이렇게 서블릿으로 API를 만드는 것은 번거롭기 때문에 Spring을 대부분의 경우에는 사용하게 된다. 우리는 원리를 알기 위해서 직접 작성해보았다.

반응형