ํ๊ตญ์ด | English
๐ฆด Spine
Spine is the execution backbone of a backend system.
Spine defines how a request is resolved, executed, and completed โ explicitly.
Spine๋ ์์ฒญ์ด ์ด๋ป๊ฒ ์คํ๋๋์ง๋ฅผ ๊ทธ๋๋ก ๋๋ฌ๋ด๋ ๋ฐฑ์๋ ํ๋ ์์ํฌ์
๋๋ค.
Spine์ ์ฝ๊ฒ ๋ฐฐ์๋ณด์ธ์
Spine์ ์คํ ๊ตฌ์กฐ์ ์ฌ์ฉ ๋ฐฉ๋ฒ์ ํ๋์ ํ์
ํ ์ ์๋ ๊ณต์ ์ฌ์ดํธ๊ฐ ์ด๋ ธ์ต๋๋ค.
Spine ๊ณต์ ์ฌ์ดํธ
Spine์ ๋ฌธ์ ์์
๋๋ถ๋ถ์ ์น ํ๋ ์์ํฌ๋ ์คํ ํ๋ฆ์ ์จ๊น๋๋ค.
- ์์ฒญ์ด ์ด๋์ ์์๋๋์ง
- ๋๊ฐ ์ธ์๋ฅผ ์์ฑํ๋์ง
- ์ธ์ ๋น์ฆ๋์ค ์ฝ๋๊ฐ ์คํ๋๋์ง
- ๋ฐํ๊ฐ์ด ์ด๋ป๊ฒ ์๋ต์ผ๋ก ๋ณํ๋๋์ง
์ด ๋ชจ๋ ๊ณผ์ ์ ํ๋ ์์ํฌ ๋ด๋ถ ๊ท์น๊ณผ ๊ด์ต, ์๋ฌต์ ๋์์ ๋ฌปํ ์์ต๋๋ค.
Spine๋ ์ด ํ๋ฆ์ ์จ๊ธฐ์ง ์์ต๋๋ค.
์คํ ์์์ ์ฑ
์์ ์ฝ๋ ๊ตฌ์กฐ๋ก ๊ณ ์ ํฉ๋๋ค.
ํ๊ตญ๊ณผ Spine
ํ๊ตญ์๋ ์์ฒด IoC + ์คํ ํ์ดํ๋ผ์ธ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง ๋ฐฑ์๋ ํ๋ ์์ํฌ๊ฐ ๊ฑฐ์ ์์ต๋๋ค.
์ ์์ ๋ถ ํ๋ ์์ํฌ์กฐ์ฐจ Spring IoC ์์ ์กฐํฉ๋ฌผ์ ๊ฐ๊น์ต๋๋ค.
NestJS, Spring, FastAPI, Django๋ ๋ชจ๋ ํด์ธ ์ค๊ณ ์ฒ ํ์ ์์
์
๋๋ค.
Spine๋ ํ๊ตญ์์ ๊ฑฐ์ ์ฒ์ ์๋๋๋,
Execution Pipeline ์ค์ฌ์ ํ๋์ ์ธ ๋ฐฑ์๋ ํ๋ ์์ํฌ์
๋๋ค.
Spine์ผ๋ก ๋ง๋ ์์ ํ๋ก์ ํธ
๐ https://github.com/NARUBROWN/spine-user-demo
Spine์ ๋ฌด์์ด ์๋๊ฐ
- โ HTTP Engine์ด ์๋๋๋ค
- โ Router ์ค์ฌ ํ๋ ์์ํฌ๊ฐ ์๋๋๋ค
- โ Annotation / Decorator ๊ธฐ๋ฐ ํ๋ ์์ํฌ๊ฐ ์๋๋๋ค
- โ Convention over Configuration์ ์ฑํํ์ง ์์ต๋๋ค
- โ Controller์ ์คํ ์ฑ
์์ ์์ํ์ง ์์ต๋๋ค
Spine๋ Execution Pipeline์
๋๋ค.
์ ์ฒด ์ํคํ
์ฒ ๊ฐ์
HTTP Transport (Echo)
โ
โผ
ExecutionContext
โ
โผ
Pipeline
โโ Router
โโ ParameterMeta Builder
โโ ArgumentResolver Chain
โโ Interceptor (preHandle)
โโ Invoker (Controller Method Call)
โโ ReturnValueHandler
โโ Interceptor (postHandle)
โ
โผ
ResponseWriter
์ด ๊ตฌ์กฐ๋ ์ค์ ์ด ์๋๋ผ ์คํ ๋ชจ๋ธ ๊ทธ ์์ฒด์
๋๋ค.
Execution Pipeline
๋ชจ๋ ์์ฒญ์ ๋ค์ ์์๋ฅผ ๋ฐ๋์ ๋ฐ๋ฆ
๋๋ค.
- Pipeline ์ง์
- Router๋ฅผ ํตํ HandlerMeta ์ ํ
- ParameterMeta ์์ฑ
- ArgumentResolver ์ฒด์ธ ์คํ
- Interceptor.preHandle
- Controller Method ํธ์ถ (Invoker)
- ReturnValueHandler ์คํ
- Interceptor.postHandle
- ResponseWriter๋ฅผ ํตํ ์๋ต ๊ธฐ๋ก
์ด ์์๋ ์จ๊ฒจ์ง์ง ์์ผ๋ฉฐ, ์๋ฌต์ ์ผ๋ก ๋ณ๊ฒฝ๋์ง ์์ต๋๋ค.
Controller ์ค๊ณ ์ฒ ํ
Spine์ ์์น
Controller๋ ์คํ ๋ชจ๋ธ์ ๋ชจ๋ฅธ๋ค.
ํ์ง๋ง ์
๋ ฅ์ ์ถ์ฒ๋ ํ์
์ผ๋ก ๋ช
์ํ๋ค.
Controller๋ ๋ค์์ ์์กดํ ์ ์์ต๋๋ค:
path.* : Path Parameter ์๋ฏธ ํ์
query.* : Query Parameter ์๋ฏธ ํ์
httperr.* : HTTP Error ์๋ฏธ ํ์
Controller๋ ๋ค์์ ์์กดํ์ง ์์ต๋๋ค:
- ExecutionContext
- Pipeline
- Router
- Resolver
- HTTP / Transport ํ์
Controller ์์
func (c *UserController) GetUser(userId path.Int) (User, error) {
if userId.Value <= 0 {
return User{}, httperr.BadRequest("์ ํจํ์ง ์์ ์ฌ์ฉ์ ID์
๋๋ค")
}
user, err := c.repo.FindByID(userId.Value)
if err != nil {
return User{}, httperr.NotFound("์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค")
}
return user, nil
}
Controller๋:
- HTTP๋ฅผ ๋ชจ๋ฆ
๋๋ค
- ์คํ ์์๋ฅผ ๋ชจ๋ฆ
๋๋ค
- ๊ฐ์ ์ถ์ฒ๋ง ๋ช
์ํฉ๋๋ค
Signature-as-Contract
Spine์์ ๋ฉ์๋ ์๊ทธ๋์ฒ๋ API ๊ณ์ฝ ๊ทธ ์์ฒด์
๋๋ค.
- ์
๋ ฅ ์์ฑ โ
ArgumentResolver
- ์ถ๋ ฅ ์ฒ๋ฆฌ โ
ReturnValueHandler
์๊ทธ๋์ฒ ๋ณ๊ฒฝ์ ๊ณง API ๋ณ๊ฒฝ์
๋๋ค.
Spine๋ ๋ค์์ ์๋์ ์ผ๋ก ๊ธ์งํฉ๋๋ค:
- โ Annotation ๊ธฐ๋ฐ ๋งคํ
- โ Convention ๊ธฐ๋ฐ ์๋ ์ถ๋ก
- โ Primitive ํ์
์ ์๋ฌต์ ์ฃผ์
Context ๋ถ๋ฆฌ
Spine๋ Context๋ฅผ ๋ ๊ณ์ธต์ผ๋ก ๋ถ๋ฆฌํฉ๋๋ค.
ExecutionContext
- ์คํ ํ๋ฆ ์ ์ด ์ ์ฉ
- Router / Pipeline ๋ด๋ถ์์๋ง ์ฌ์ฉ
- Controller ๋ฐ Resolver์ ๋
ธ์ถ โ
RequestContext
- ์
๋ ฅ ํด์ ์ ์ฉ (Path / Query / Body)
- ArgumentResolver์์๋ง ์ฌ์ฉ
- Controller์ ๋
ธ์ถ โ
Path Parameter Binding Rule
Spine์ Path Parameter ๋ฐ์ธ๋ฉ์ ์์ ๊ธฐ๋ฐ(order-based) ์
๋๋ค.
์ด๋ Go ์ธ์ด ์ ์ฝ์ ๊ณ ๋ คํ ์๋์ ์ด๊ณ ๋ช
์์ ์ธ ๊ณ์ฝ์
๋๋ค.
๊ท์น
Route Path Key ์ ์ธ ์์
=
Controller ์๊ทธ๋์ฒ์ path.* ํ๋ผ๋ฏธํฐ ์ ์ธ ์์
์์
// Route
/users/:userId/posts/:postId
// Controller
func GetPost(userId path.Int, postId path.Int)
์ ์ฑ
- ์ด๋ฆ ๋งค์นญ โ
- annotation โ
- primitive ํ์
โ
- ์์ ๋ถ์ผ์น ์ ์ฆ์ ์คํจ (Fail Fast)
Query ์ฒ๋ฆฌ ์์น
์๋ฏธ๊ฐ ๊ณ ์ ๋ Query
func ListUsers(p query.Pagination)
Spine๊ฐ ์ ๊ณตํ๋ ์๋ฏธ ํ์
์ ์ฌ์ฉํฉ๋๋ค.
๊ฐ๋ณ Query
func SearchUsers(q query.Values)
- Raw Query View ์ ๊ณต
- ์ฌ์ฉ์๊ฐ ์ง์ ํด์
- DTO ์๋ ๋งคํ โ
ReturnValueHandler & ResponseWriter
Controller๋ ๊ฐ์ ๋ฐํํฉ๋๋ค.
return User{...}
์๋ต ์์ฑ์ ์ ๋ถ ReturnValueHandler๊ฐ ๋ด๋นํฉ๋๋ค.
Transport๋ ResponseWriter๋ง ๊ตฌํํฉ๋๋ค.
License
MIT