GoLang-REST API

wldbs._.·2024년 8월 27일
1

REST API & Go

목록 보기
3/5
post-thumbnail

Go와 REST API

1. 패키지와 데이터

package main

import (
	"encoding/json" // 데이터를 JSON 형식으로 인코딩/디코딩하기 위한 패키지
	"net/http"      // HTTP 프로토콜을 이용한 서버/클라이언트 구현 위한 패키지
	"sort"          // 데이터를 정렬하는 함수를 제공하는 패키지
	"strconv"       // 문자열을 기본 데이터 타입으로 변환하는 기능 제공

	"github.com/gorilla/mux" // 강력한 URL 라우팅 기능을 제공하는 외부 패키지
)

type Student struct { // 학생의 정보를 나타내는 구조체
	Id    int
	Name  string
	Age   int
	Score int
}

1) REST API를 구현할 때 주로 사용하는 패키지는 다음과 같다:

  • net/http: HTTP 서버와 클라이언트 기능을 제공
  • encoding/json: JSON 데이터의 인코딩 및 디코딩을 처리
  • github.com/gorilla/mux: 강력한 URL 라우팅 및 디스패치 기능을 제공, 요청 URL과 관련된 핸들러를 매핑하는 데 사용
    • net/http 보다 더 유연하고 강력한 URL 매개변수 추출과 라우팅 규칙을 제공
    • Package gorilla/mux implements a request router and dispatcher for matching incoming requests to their respective handler.
    • Gorilla is a web toolkit for the Go programming language that provides useful, composable packages for writing HTTP-based applications.

2) 데이터 모델은 구조체(Struct)를 사용하여 정의


2. HTTP 라우터 및 핸들러 설정

// 전역 변수 students : 학생의 ID를 키로 하여 Student 객체를 저장
var students map[int]Student

// 여기서는 고유 식별자의 역할을 하는 Id 필드가 자연스럽게 키로 사용된다!

var lastId int // 가장 최근에 추가된 마지막 학생의 ID를 추적
// 핸들러는 HTTP 요청을 받아서 그에 대응하는 작업을 수행하고, 결과를 클라이언트에게 돌려주는 역할

func MakeWebHandler() http.Handler { // 웹 서버의 요청을 처리할 라우터를 설정하고 반환

	mux := mux.NewRouter() // gorilla/mux 생성 [새로운 라우터 생성]
	// client가 특정 URL로 요청을 보낼 때, 해당 URL에 맞는 핸들러 함수를 찾아 실행해주는 것이 라우터의 역할

	// 1. 학생 목록 조회하는 핸들러 함수와 경로 등록
    // -> "/students" 경로로 "GET" 요청이 들어올 때 GetStudentListHandler 함수가 호출되도록 설정
	mux.HandleFunc("/students", GetStudentListHandler).Methods("GET")

	//2. 특정 학생 정보 조회
	mux.HandleFunc("/students/{id:[0-9]+}", GetStudentHandler).Methods("GET")

	// 3. 학생 데이터 추가
	mux.HandleFunc("/students", PostStudentHandler).Methods("POST")

	// 4. 학생 데이터 삭제
	mux.HandleFunc("/students/{id:[0-9]+}", DeleteStudentHandler).Methods("DELETE")

	// 초기 학생 데이터 설정
	students = make(map[int]Student)

	// 임시 학생 데이터 두 개 생성해서 저장
	students[1] = Student{1, "aaa", 16, 87}
	students[2] = Student{2, "bbb", 18, 98}

	lastId = 2

	return mux
}
  • gorilla/mux를 사용하여 다양한 HTTP 경로에 대한 요청을 처리할 핸들러 함수를 등록
  • MakeWebHandler 함수는 HTTP 요청을 처리할 각 경로별 핸들러 함수를 설정하고, 이렇게 설정된 라우터를 반환하는 역할을 하는 함수
    • MakeWebHandler는 기본적으로 "핸들러의 컨테이너"로 볼 수 있으며, 각 URL 경로에 대해 어떤 핸들러가 요청을 처리할지를 설정
  • gorilla/mux 라이브러리를 사용하여 mux라는 라우터 객체를 생성하고, 각 HTTP 요청의 경로(URL path)와 해당 요청을 처리할 함수(handler function)를 연결

핸들러 함수는 HTTP 요청을 처리하고, 응답을 반환하는 함수

  • 이 함수들은 특정 경로에 대한 요청을 받아들이고, 요청에 따라 필요한 작업을 수행한 후 클라이언트에게 응답을 보낸다.
  • MakeWebHandler 함수에서는 여러 핸들러 함수를 각각의 경로에 등록하고 있다.

mux 라우터는 클라이언트로부터 들어오는 요청의 URL을 분석하고, 등록된 경로와 일치하는 핸들러 함수를 찾아 호출

  • 각 경로와 그에 대응하는 핸들러 함수는 mux.HandleFunc를 사용하여 등록된다
  • 핸들러 함수란?
    : HTTP 요청을 처리하는 기본 구성 요소, net/http 패키지에 정의된 http.Handler 인터페이스를 구현
    : 요청 정보를 분석하고, 적절한 로직을 수행한 뒤 응답을 반환하는 일련의 과정을 담당
    : 이를 위해 요청 객체(http.Request)에서 URL 파라미터, 헤더, 본문 데이터 등을 읽어 로직을 처리
  • 웹 서버의 특정 요청(URL 경로, HTTP 메소드 등)에 응답하는 데 사용
    • 핸들러 함수는 특정 시그니처를 따르며, 이 시그니처는 두 개의 파라미터를 받는다:
      - w http.ResponseWriter: 클라이언트에게 HTTP 응답을 보낼 수 있는 인터페이스, 이 객체를 통해 응답 상태 코드/헤더/본문을 설정할 수 있다.
      - r *http.Request: 클라이언트로부터 받은 요청의 모든 정보를 담고 있다. 이 객체에서는 요청 메소드, 헤더, URL 파라미터, 본문 등에 접근할 수 있다.
    • http.Handle 또는 http.HandleFunc를 사용하여 특정 경로와 연결된다.
      - http.HandleFunc는 함수를 직접 등록할 수 있게 해주며, 더 간단하게 사용할 수 있다.

3. 핸들러 함수 구현

type Students []Student
// Students는 []Student라는 슬라이스 타입을 기반으로 만들어진 사용자 정의 타입
// []Student는 Student 구조체 인스턴스의 컬렉션, 여러 학생의 데이터를 그룹화하고 관리
// Students는 []Student와 같은 데이터를 가지며 특정 메서드를 추가할 수 있는 기능을 제공

/*
sort 패키지에 정의된 sort.Interface 인터페이스 구현
-> sort 패키지의 함수들을 사용하여 해당 슬라이스를 정렬할 수 있다.
  - sort.Interface는 Go 언어의 표준 라이브러리 sort 패키지에 정의된 인터페이스
  - 아래 세 메서드는 sort 패키지의 여러 정렬 함수들이 데이터를 정렬할 때 필요한 연산을 제공
  - 사용자가 자신의 타입에 대해 sort.Interface를 구현하면,
    -> 그 타입의 슬라이스에 대해 sort.Sort 등의 함수들을 사용할 수 있다
*/

func (s Students) Len() int { // 1) Len() int: 슬라이스의 길이 반환
	return len(s)
}
func (s Students) Swap(i, j int) { // 2) Swap(i, j int): 슬라이스 내 두 요소의 위치 교환
	s[i], s[j] = s[j], s[i]
}
func (s Students) Less(i, j int) bool { // 3) Less(i, j int) bool: 두 요소의 순서를 비교
	return s[i].Id < s[j].Id
	// i번째 요소가 j번째 요소보다 "작은지" 여부를 판단하여 정렬 순서를 결정
	// Id를 기준으로 학생들을 오름차순(ascend)으로 정렬
}
// 1. 저장된 모든 학생 정보를 JSON 형식으로 반환하는 핸들러
// /students 경로로 "GET" 요청이 들어왔을 때 호출되는 함수
func GetStudentListHandler(w http.ResponseWriter, r *http.Request) {
	list := make(Students, 0) // 길이가 0인 슬라이스 생성
	for _, student := range students {
		list = append(list, student)
		sort.Sort(list) // ID를 기준으로 학생 목록을 정렬
		w.WriteHeader(http.StatusOK)
	}
	// students 맵에서 모든 학생 데이터를 Students 슬라이스에 추가한 다음, 이를 Id 순서대로 정렬
	// 응답의 상태 코드를 200 OK로 설정
	w.Header().Set("Content-Type", "application/json") // 응답의 콘텐츠 타입을 application/json으로 설정
	json.NewEncoder(w).Encode(list)                    // 정렬된 학생 목록을 JSON 형식으로 인코딩하여 응답 본문에 작성
}

// 2. 요청된 ID에 해당하는 학생 정보를 JSON 형식으로 반환
func GetStudentHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)               // mux.Vars() 호출해 인수 가져온다
	id, _ := strconv.Atoi(vars["id"]) // URL에서 id 파라미터를 정수로 변환
	student, ok := students[id]

	if !ok {
		w.WriteHeader(http.StatusNotFound) // 학생 데이터를 찾지 못한 경우 404 에러 반환
		return
	}

	w.WriteHeader(http.StatusOK)                       // 성공 상태 코드 설정
	w.Header().Set("Content-Type", "application/json") // 내용 타입 설정
	json.NewEncoder(w).Encode(student)                 // 학생 데이터를 JSON 형식으로 인코딩 후 응답
}
// 3. 새로운 학생 데이터를 받아서 시스템에 추가
func PostStudentHandler(w http.ResponseWriter, r *http.Request) {
	var student Student // 새로운 학생 정보를 저장할 Student 타입 변수 선언

	// 요청 본문에서 JSON 형식의 데이터를 읽어와 Student 구조체로 디코딩
	err := json.NewDecoder(r.Body).Decode(&student)

	if err != nil {
		w.WriteHeader(http.StatusBadRequest) // JSON 디코딩 실패 시, HTTP 400 (Bad Request) 상태 코드 반환
		return
	}
	lastId++            // 글로벌 lastId 변수를 증가시켜 새로운 학생에게 유니크한 ID 할당
	student.Id = lastId // 새로운 ID를 학생의 Id 필드에 설정

	students[lastId] = student // students 맵에 새 학생 정보를 추가

	w.WriteHeader(http.StatusCreated) // 데이터 추가 성공 시, HTTP 201 (Created) 상태 코드 반환
}

// 4. 주어진 ID에 해당하는 학생 데이터를 제거
func DeleteStudentHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)               // URL 파라미터에서 데이터 추출
	id, _ := strconv.Atoi(vars["id"]) // URL 경로에 포함된 id 파라미터를 정수로 변환

	// students 맵에서 해당 ID를 키로 사용하여 학생 데이터 존재 여부 확인
	_, ok := students[id]

	if !ok {
		w.WriteHeader(http.StatusNotFound) // 해당 ID의 학생이 존재하지 않는 경우, HTTP 404 (Not Found) 반환
		return
	}
	// 해당 ID의 학생 데이터가 존재하는 경우, 맵에서 해당 학생 정보 삭제
	delete(students, id)
	w.WriteHeader(http.StatusOK) // 성공적으로 학생 데이터를 삭제한 경우, HTTP 200 (OK) 반환
}
  • 각 HTTP 요청에 대응하는 핸들러 함수를 구현
    • 함수들은 요청을 처리하고, 적절한 HTTP 응답을 반환

HTTP 응답이란?
: 클라이언트의 요청에 대해 서버가 반환하는 데이터로, 주요 구성 요소는 상태 코드/헤더/응답 본문
: 각 요소는 요청에 대한 결과 및 추가 정보를 제공

1) 상태 코드 (Status Code)
: 상태 코드는 요청이 성공적이었는지, 그리고 어떤 종류의 응답을 반환하는지를 나타내는 3자리 숫자
: 상태 코드의 첫 번째 숫자에 따라 다섯 가지 주요 범주로 나뉜다:

  • 1xx (정보 응답): 요청을 받았으며 프로세스를 계속한다.
  • 2xx (성공): 요청을 성공적으로 받았으며 이해하고 처리했다.
    - 200 OK: 요청 성공적으로 처리.
    - 201 Created: 요청이 성공적으로 이루어져 새 리소스가 생성됨.
    - 204 No Content: 요청은 성공적이지만 반환할 컨텐츠가 없음.
  • 3xx (리다이렉션): 추가 조치가 필요하여 요청을 완료해야 한다.
    - 301 Moved Permanently: 요청된 리소스가 영구적으로 새 위치로 이동됨.
    - 302 Found: 요청된 리소스가 일시적 다른 위치로 이동됨.
  • 4xx (클라이언트 에러): 클라이언트가 잘못된 요청을 보냈을 때.
    - 400 Bad Request: 서버가 요청을 이해할 수 없음.
    - 404 Not Found: 요청한 리소스를 찾을 수 없음.
    - 401 Unauthorized: 인증 없이 요청이 이루어졌음.
  • 5xx (서버 에러): 서버가 유효한 요청을 처리하지 못했을 때.
    - 500 Internal Server Error: 서버 내부 오류.
    - 503 Service Unavailable: 서비스 이용 불가.

2) 헤더 (Headers)
: HTTP 헤더는 요청과 응답에 대한 추가 정보를 제공한다.
: 이는 데이터 형식, 인코딩, 캐싱 정책, 인증 정보 등을 포함할 수 있다. 몇 가지 중요한 응답 헤더는 다음과 같다:

Content-Type: 응답 본문의 미디어 타입을 설명 (예: application/json).
Set-Cookie: 클라이언트에 쿠키를 설정하도록 지시
Cache-Control: 응답 데이터의 캐싱 방법을 정의
Access-Control-Allow-Origin: CORS(Cross-Origin Resource Sharing) 정책을 지정

3) 응답 본문 (Response Body)
: 응답 본문은 클라이언트로 반환되는 실제 데이터를 포함한다.
: 이 데이터는 HTML, JSON, XML 등 다양한 형식일 수 있다.

  • API 응답의 경우 주로 JSON 형식으로 데이터가 구성
func main() {
	http.ListenAndServe(":3000", MakeWebHandler()) // 3000번 포트에서 HTTP 서버 시작
	// MakeWebHandler에서 반환된 핸들러를 사용하여 요청 처리
	// http://localhost:3000/students로 요청을 보내면, 서버는 두 개의 미리 정의된 학생 데이터를 JSON 형식으로 응답
	// -> 이 데이터는 Id 기준으로 정렬된 상태로 반환
}

main 함수에서 http.ListenAndServe(":3000", MakeWebHandler())를 호출함으로써

  • 3000번 포트에서 HTTP 서버를 시작
  • 이 때 MakeWebHandler에서 생성 및 반환된 mux 라우터가 서버의 요청 처리 로직을 담당

서버는 클라이언트로부터 들어오는 요청을

  • mux 라우터를 통해 적절한 핸들러 함수로 라우팅하며,
  • 각 핸들러는 요청에 따라 처리 후 JSON 형태의 응답을 반환

4. 실행 과정

[1] 서버 시작
: main 함수에서 net/http 패키지에 있는 http.ListenAndServe를 호출하여 서버를 시작

  • 이 함수는 지정된 포트에서 들어오는 HTTP 요청을 기다린다.
  • HTTP 서버를 시작하고 지정된 주소에서 들어오는 클라이언트 요청을 처리하는 데 사용된다.
    func ListenAndServe(addr string, handler http.Handler) error
    • addr: 서버가 수신 대기할 호스트 주소와 포트 번호를 문자열로 지정
      • 포트 번호만 지정하려면 "localhost:8080"과 같이 사용하거나,
      • 모든 인터페이스에서 수신 대기하려면 ":8080"과 같이 콜론을 포함한 포트 번호를 사용
    • handler: http.Handler 인터페이스를 구현하는 객체를 지정
      • 서버로 들어오는 모든 HTTP 요청을 처리
      • nil을 전달하면 기본 먹스(http.DefaultServeMux)가 사용된다.
    • error: 서버 실행 중 발생하는 오류를 반환
      • 보통은 서버가 실패하거나 종료될 때 반환되는 에러

[2] 요청 수신 및 라우팅
: 클라이언트로부터 요청이 들어오면 gorilla/mux 라우터가 URL을 분석하고 적절한 핸들러 함수로 요청을 전달

[3] 요청 처리
: 핸들러 함수는 요청 데이터를 처리하고, 결과를 JSON 등의 형태로 클라이언트에게 응답

[4] 응답 반환
: 클라이언트는 서버로부터 응답을 받고, 이 데이터를 사용하여 웹 페이지를 업데이트하거나 다른 처리를 진행


5. 클라이언트 상호작용

클라이언트는 웹 브라우저, 모바일 앱, 또는 다른 서버일 수 있으며, REST API를 통해 서버와 데이터를 주고받는다.
클라이언트는 HTTP 메소드(GET, POST, DELETE 등)를 사용하여 API에 요청을 보낸다.

profile
공부 기록용 24.08.05~ #LLM #RAG

0개의 댓글