Go web 4. RESTFUL API

JINSOO PARK·2021년 10월 26일
0

Go 로 만드는 웹

목록 보기
3/16

REST란?

Representational State Transfer의 약자
자원을 이름(자원의 표현)으로 구분하여 해당 자원의 상태를 주고받는 모든 것을 의미한다.

  • 자원: 문서나 그림, 데이터 등 해당 소프트웨어가 관리하는 모든것을 의미한다.

  • 자원의 표현: 그 자원을 표현하기 위한 이름
    ex) DB의 학생 정보가 자원일 때, 'students'를 자원의 표현으로 정한다.

REST의 구체적인 개념

웹 사이트의 이미지,텍스트, DB 내용 등 모든 자원에 고유한 ID인 HTTP URI(Uniform resource Identifier)을 부여하고, HTTP Method(POST, GET, PUT,DELETE)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것을 의미한다.

  • CRUD Operation
  1. Create : 생성 (POST)
  2. Read : 조회 (GET)
  3. Update: 수정 (PUT)
  4. Delete: 삭제 (DELETE)
  5. HEAD: heade정보 조회 (HEAD)

REST API란?

API란?

Application Programming interface의 약자로 데이터와 기능의 집합을 제공하여 컴퓨터 프로그램간 상호작용을 촉진하며, 서로 정보를 교환 가능 하도록 하는것

REST API란?

REST 기반으로 서비스 API를 구현한 것

REST API의 특징

  • 사내 시스템들도 REST 기반으로 시스템을 분산해 확장성과 재사용성을 높여 유지보수 및 운용을 편리하게 할 수 있다.
  • REST는 HTTP 표준을 기반으로 구현하므로, HTTP를 지원하는 프로그램 언어로 클라이언트와 서버를 구현할 수 있다.

RESTful이란?

RESTful은 일반적으로 REST라는 아키텍처를 구현하는 웹 서비스를 나태내기 위해 사용되는 용어이다. REST API를 제공하는 웹 서비스를 "RESTful"하다라고 할 수 있다.

RESTful은 REST를 REST답게 쓰기 위한 방법으로, 누군가가 공식적으로 발표한 것이 아니다. 즉 REST의 원리를 따르는 시스템은 RESTful이란 용어로 지칭된다.

RESTful의 목적

  • 이해하기 쉽고 사용하기 쉬운 REST API를 만드는 것
  • RESTful한 API를 구현하는 근본적인 목적이 성능 향상에 있는것이 아니라 일관적인 컨벤션을 통한 API의 이해도 및 호환성을 높이는 것이 주 동기이니, 성능이 중요한 상황에서는 굳이 RESTful한 API를 구현할 필요는 없다.

링크텍스트


ex) main

package main

import (
	"net/http"
	"web3/myapp"
)

func main() {
	http.ListenAndServe(":3000", myapp.NewHandler())
	// 해당 네트워크 주소를 수신한다음 요청을 처리하기 위해 핸들러를 호출
}

ex) myapp

package myapp

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/gorilla/mux"
)

// json형태의 유저 스트럭스
type User struct {
	ID        int       `json:"id"`
	FirstName string    `json:"first_name"`
	LastName  string    `json:"last_name"`
	Email     string    `json:"email"`
	CreatedAt time.Time `json:"created_at"`
}

// 유저 정보를 들고있는 맵
var userMap map[int]*User

var lastID int // user id를 넣을 변수

// / 메인페이지에 출력할 메세지
func indexHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hello World")
	// w에 Hello World 를 집어서 출력
}

// /users에 출력할 메세지
func usersHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Get UserInfo by /users/{id}")
	// "Get UserInfo by /users/{id} 출력
}

// 유저 정보를 얻는다.
func getUserInfoHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	// 리퀘스트에 대한 경로 변수가 있는 경우 해당 변수를 반환
	id, err := strconv.Atoi(vars["id"])
	// string을 int로
	if err != nil { // 에러가 났을때
		w.WriteHeader(http.StatusBadRequest)
		//제공된 http 리스폰스 헤더를 전송한다.
		fmt.Fprint(w, err)
		return
	}
	user, ok := userMap[id] // ok는 부울값
	if !ok {                // ok가 아닐때
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, "No User Id:", id)
		return
	}

	w.Header().Add("Content-Type", "application/json")
	// WriteHeader에서 보낼 헤더 맵을 반환
	// add(key, value) 를 헤더에 추가
	w.WriteHeader(http.StatusOK)
	data, _ := json.Marshal(user)
	// user의 JSON 인코딩 반환
	fmt.Fprint(w, string(data))
}

// 실제로 유저를 생성하는 코드
func createUsersHandler(w http.ResponseWriter, r *http.Request) {
	user := new(User)
	// User타입의 포인터 user 생성
	err := json.NewDecoder(r.Body).Decode(user)
	// NewDecoder: 바디에서 읽은 새 디코더 반환
	// Decode : json 인코딩 값을 user에 저장
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err)
		return
	}
	// 유저 생성
	lastID++ // 유저가 생성될때 마다 증가
	user.ID = lastID
	user.CreatedAt = time.Now() //현재 시간
	userMap[user.ID] = user

	w.Header().Add("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)

	data, _ := json.Marshal(user)
	fmt.Fprint(w, string(data))
}

func deleteUserHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err)
		return
	}
	_, ok := userMap[id]
	if !ok {
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, "No User ID:", id)
		return
	}
	delete(userMap, id)
	// 해당 키를 가진 요소를 맵에서 삭제한다.
	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, "Deleted User ID:", id)
}

func updateUserHandler(w http.ResponseWriter, r *http.Request) {
	updateUser := new(User)
	err := json.NewDecoder(r.Body).Decode(updateUser)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err)
		return
	}
    //값이 없을때
	user, ok := userMap[updateUser.ID]
	if !ok {
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, "No User ID:", updateUser.ID)
		return
	}
    //빈값이 아닐때
	if updateUser.FirstName != "" { 
		user.FirstName = updateUser.FirstName
	}
	if updateUser.LastName != "" {
		user.LastName = updateUser.LastName
	}
	if updateUser.Email != "" {
		user.Email = updateUser.Email
	}
	w.Header().Add("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	data, _ := json.Marshal(user)
	fmt.Fprint(w, string(data))
}

// 새로운 핸들러를 만들어주는 핸들러
func NewHandler() http.Handler {
	userMap = make(map[int]*User)
	// 유저맵 생성
	lastID = 0

	mux := mux.NewRouter()
	// 고릴라 먹스를 이용하여 새 라우터 인스턴스 반환

	mux.HandleFunc("/", indexHandler)
	mux.HandleFunc("/users", usersHandler).Methods("GET")
	//.Methods("메소드")고릴라 먹스 기능
	// 메소드에 따라 핸들러가 바껴야됨
	// GET 일때 해당 핸들러 사용
	mux.HandleFunc("/users", createUsersHandler).Methods("POST")
	// POST 일때 해당 핸들러 사용
	mux.HandleFunc("/users/{id:[0-9]+}", getUserInfoHandler).Methods("GET")
	//{id:[0-9]+} 고릴라 먹스가 자동으로 id를 추출
	mux.HandleFunc("/users/{id:[0-9]+}", deleteUserHandler).Methods("DELETE")
	// DELETE 매소드를 썻을때 deleteUserHandler 핸들러 사용
	mux.HandleFunc("/users", updateUserHandler).Methods("PUT")
	// PUT 메소드를 썻을때 업데이트
	return mux
}


메인 페이지


/users 페이지


/users/id를 입력했을때

현재 아무 정보도 없음


POST 유저 정보 생성

POST 매서드를 이용해 json형태로 입력 후 send


/users/1 페이지에 표시 되는것을 알 수 있다.


PUT 업데이트

해당 아이디를 입력하고 put 매서드를 이용해 업데이트


DELETE 삭제


해당 아이디를 delete 매서드로 삭제

profile
개린이

0개의 댓글