아직 golang에 대해서 완벽하게 습득되지 않은 상태에서 golang을 활용한 backend 개발에 대해서 작성해보는 글로, 완전완전 기초만 다뤄볼 예정이다.
※ 들어가기 전 알아야 할 요소?
★ 12가지 app 개발 방법론
The Twelve-Factor App (한국어)
배경 이 문서에 기여한 사람들은 수백개 앱의 개발과 배포에 직접 참여했으며, Heroku 플랫폼을 통해서 방대한 앱의 개발, 운영, 확장을 간접적으로 관찰했다. 이 문서는 실제로 쓰이는 다양한 SaaS
12factor.net
★ CRUD
CRUD
C : CREATE -> POST(PUT)
R : READ -> GET
U : UPDATE -> PATCH/PUT
D : DELETE -> DELETE
★ stateless (상태유지 x) 구조 / stateful (상태유지) 구조
stateful
상태유지 구조
클라이언트 - 서버 관계에서 서버가 클라이언트의 상태를 보존하는 구조
서버에서 클라이언트의 데이터를 저장함
그러나 서버 부하가 크고, 만약 해당 서버가 멈추거나, 못쓰게 될 경우 다른 서버를 쓰게 될 때 다른 서버는 클라이언트에 대한 데이터를 데이터를 가지고 있지 않으므로 클라이언트 데이터의 초기화 문제가 발생한다.
대규모 트래픽 대처 능력이 현저히 떨어진다(클라이언트가 몰릴수록 서버의 부하는 커짐)
ex> tcp 3-way handshaking
stateless
무상태 구조
클라이언트 - 서버 관계에서 서버가 클라이언트의 상태를 보존하지 않음, 즉 서버는 오로지 단순 요청에 대한 응답을 보내는 역할만을 수행하며, 상태관리는 전적으로 클라이언트가 하는 구조임
서버가 단순 응답만 하므로 클라이언트 - 서버 통신 시, 통신에 필요한 모든 상태 관련 정보들을 클라이언트가 가지고 있다가 서버와 통신할 때 정보를 보내는 구조임
서버 부하 x, 대규모 트래픽에 원활하게 대처 가능 (서버가 바뀌어도, stateful과 달리 응답에는 문제가 없음, stateful 구조의 경우 서버가 바뀌면 응답이 달라질 수 있음)
ex> udp, http
Basic code
아주 기본적인 웹 사이트를 만들어보자.
golang으로 backend api를 구현하기 위해 아주 중요한 패키지가 존재한다.
http package - net/http - Go Packages
Discover Packages Standard library net http Version: go1.23.3 Opens a new window with list of versions in this module. Published: Nov 6, 2024 License: BSD-3-Clause Opens a new window with license information. Imports: 46 Opens a new window with list of imp
pkg.go.dev
import로 "net/http"를 불러온 다음 http 패키지의 메소드를 이용해서 요청에 대한 응답을 처리한다.
먼저 전체 코드를 살펴보자.
import (
"log"
"net/http"
)
type server struct {
addr string // type server struct를 선언함으로서 server라는 타입을 만들어냄
}
// responsewriter -> http.ResponseWriter 인터페이스는 HTTP 응답을 위한 인터페이스
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// w.Write([]byte("Hello from my server"))
switch r.Method {
case http.MethodGet: // get 요청일 경우 / 하드코딩해서 "get" 이라고 작성해도 됨
switch r.URL.Path {
case "/":
w.Write([]byte("index"))
return // return을 하지 않으면 다음 case로 넘어가기 때문에 return을 해줘야 함
// 기본적으로 고랭은 switch문에 자동 break가 존재하지만 바깥쪽에도 switch가 걸려있기 때문에 return
case "/users":
w.Write([]byte("user"))
return
default:
w.Write([]byte("404"))
return
}
}
// w를 이용해서 response를 받고, r을 이용해서 request 객체를 받아 객체에 속한 함수 / 변수 사용
}
func main() {
s := &server{addr: ":8080"} // server 구조체를 생성하고 주소를 할당
if err := http.ListenAndServe(s.addr, s); err != nil {
log.Fatal(err)
}
}
간단하게 "/"와 "/users" 부분만 구현했다. 만약 해당 api 경로로 들어갈 경우, 사이트 창에 "index"라는 글자 또는 "user"라는 글자, 만약 에러가 발생했다면 "404"가 뜰 것이다.
type server struct {
addr string
}
server라는 구조체를 선언한다.
해당 구조체에는 string 형 변수 addr이 속해 있다. 약간 인터페이스 같은 친구라고 생각하면 될듯
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// w.Write([]byte("Hello from my server"))
switch r.Method {
case http.MethodGet: // get 요청일 경우 / 하드코딩해서 "get" 이라고 작성해도 됨
switch r.URL.Path {
case "/":
w.Write([]byte("index"))
return // return을 하지 않으면 다음 case로 넘어가기 때문에 return을 해줘야 함
// 기본적으로 고랭은 switch문에 자동 break가 존재하지만 바깥쪽에도 switch가 걸려있기 때문에 return
case "/users":
w.Write([]byte("user"))
return
default:
w.Write([]byte("404"))
return
}
}
// w를 이용해서 response를 받고, r을 이용해서 request 객체를 받아 객체에 속한 함수 / 변수 사용
}
ServeHTTP 메소드를 선언한다. 해당 메소드는 server 구조체의 인스턴스에 속한 메소드로, server 구조체를 가리키는 포인터를 참조하여 ServeHTTP 메소드가 호출될 수 있다. (구조체의 인스턴스에 속한 메소드 정의 -> 포인터로 호출 가능)
해당 메소드를 선언함으로서 http.Handler 인터페이스를 구현한다. (package -> net/http에 속한 인터페이스)
w와 r은 매개변수
그냥 간단하게 말해서 server 타입의 메소드란 의미이다.
func main() {
s := &server{addr: ":8080"}
http.ListenAndServe(s.addr, s)
}
&server{addr: ":8080"}
server 구조체의 인스턴스 생성 (addr 변수 할당 -> "8080")
해당 인스턴스의 포인터를 &로 통해 생성한 후 변수 s에 할당한다.
http.ListenAndServe(s.addr, s)
http.ListenAndServe는 두번째 인자로 http.Handler 인터페이스를 받는데, 이 인터페이스는 ServeHTTP 메소드를 통해 구현되어 있다. s에 server 구조체의 인스턴스 포인터를 할당하였으므로, s를 통해 addr로 접근해서 해당 주소에서 서버를 시작하고, 해당 서버에서 오는 모든 요청(req)을 받아 s가 가리키는 인스턴스의 ServeHTTP 메소드로 넘기게 된다.
그 후 요청은 ServeHTTP에서 사용자 정의대로 처리하게 된다.
경로에 따라 페이지에 문자열을 출력하도록 하였으므로, go run main.go를 입력하고 서버를 실행시켜 보자.
go run main.go를 실행하면 아래와 같이 뜰 것이다.
헷갈렸던 개념들...
defer
defer로 선언된 코드는 해당 코드가 속한 함수가 종료되기 직전까지 지연되다가, 함수에 속한 코드가 모두 실행되고 나서야 실행된다는 의미이다.
switch
golang에서 switch는 case에 걸리면 바로 break가 걸려서 그 이후 case 문을 실행하지 않기 때문에, 일일이 break 문을 설정해 줄 필요는 없다.
var p *int = &x
이렇게 작성되어 있을 경우, x의 메모리 주소를 p 포인터 변수에 할당한다는 의미, 즉 p는 포인터 변수가 된다.
*int는 int 형 값을 가리키는 포인터를 의미한다. x에 할당된 값은 int이고, x의 메모리 주소는 &x로 표현
references
http package - net/http - Go Packages
Discover Packages Standard library net http Version: go1.23.3 Opens a new window with list of versions in this module. Published: Nov 6, 2024 License: BSD-3-Clause Opens a new window with license information. Imports: 46 Opens a new window with list of imp
pkg.go.dev
https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-Stateful-Stateless-%EC%A0%95%EB%A6%AC
🌐 아주 쉽게 이해하는 Stateful / Stateless 차이
Stateful 과 Stateless 차이점 웹 공부를 하다보면 클라이언트(Client)와 서버(Server)간의 통신을 상태유지(Stateful) 하느냐, 상태유지하지않음(Stateless) 으로 하느냐 라는 말귀를 한번쯤은 들어본 적이 있
inpa.tistory.com
'개발' 카테고리의 다른 글
[golang] go channel, go routine (1) | 2024.11.27 |
---|---|
[golang] A tour of Go exercise (0) | 2024.11.19 |
[golang] method, interface, pointer 개념 다시잡기 (0) | 2024.11.18 |
[Frontend] 이미지 최적화해서 보내기 (0) | 2024.09.23 |
github actions와 aws를 이용한 cicd pipeline 구축 (2) | 2024.09.05 |