MMeshOps
AI와 함께하는 Go 백엔드와 실전 아키텍처
6예상 10입문

백그라운드 작업과 취소 버튼: 고루틴과 Context

수만 명의 접속을 가볍게 처리하는 고루틴의 기본 개념과, 실행 중인 작업을 안전하게 취소하는 Context의 역할을 배웁니다.

유명 맛집의 주방에 요리사가 단 한 명뿐이라면, 아무리 요리 속도가 빨라도 100명의 손님이 동시에 몰아닥쳤을 때 식당은 마비될 것입니다. 백엔드 서버도 마찬가지입니다. 1만 명의 유저가 동시에 접속했을 때 서버가 뻗지 않고 유연하게 대응하려면 요리사를 순식간에 1만 명으로 늘렸다가 줄이는 마법이 필요합니다. Go 언어가 전 세계 백엔드 시장을 제패한 강력한 무기가 바로 0.1초 만에 요리사를 1만 명씩 고용하는 고루틴(Goroutine)입니다. 더불어, 성격 급한 손님이 요리 도중 주문을 취소하고 나갔을 때 요리사가 즉시 볶음밥 만들기를 멈출 수 있게 해주는 마법의 리모컨 Context에 대해 알아봅시다.

일꾼을 1만 명으로 복제하기: 고루틴 (Goroutine)

보통 파이썬이나 자바로 백엔드 서버를 만들 때, 동시에 1개의 요청을 처리하기 위해 운영체제(OS)로부터 '스레드(Thread)'라는 뚱뚱하고 무거운 일꾼을 하나 배정받습니다. 이 뚱뚱한 일꾼은 메모리도 많이 차지하고 고용하는 데 시간도 오래 걸려서, 일꾼이 수천 명으로 늘어나면 서버의 메모리가 먼저 터져버립니다.

하지만 Go 언어는 운영체제의 눈을 피해 자체적으로 '고루틴'이라는 엄청나게 가볍고 날렵한 일꾼을 수백만 명씩 찍어낼 수 있는 사기적인 능력을 갖추고 있습니다.

무거운 스레드와 가벼운 고루틴

구분일반적인 스레드 (Thread)Go 언어의 고루틴 (Goroutine)
메모리 무게일꾼 1명당 약 1~2MB의 메모리를 먹음 (매우 뚱뚱함)일꾼 1명당 고작 2KB의 메모리만 먹음 (깃털처럼 가벼움)
수용 능력보통 1만 명만 넘어가도 서버가 허덕이며 뻗을 준비를 함수십만 명, 수백만 명을 고용해도 서버가 웃으면서 버팀
사용 방법스레드를 생성하고 관리하는 코드가 아주 길고 복잡함함수 앞에 'go' 라는 단어 딱 2글자만 붙이면 됨

회원가입이 완료된 후, 유저에게 환영 이메일을 보내려면 약 2초의 시간이 걸립니다. 만약 2초 동안 서버가 멈춰서 기다린다면 유저는 답답함을 느낄 것입니다. 이때 `go sendEmail()` 이라고 함수 앞에 단 2글자만 붙여주면, 메인 서버는 즉시 유저에게 "가입 완료!" 화면을 띄워주고, 뒤편에서는 고루틴 일꾼이 백그라운드에서 여유롭게 이메일을 발송합니다.

주문 취소 리모컨: `context.Context`

맛집에서 볶음밥을 시킨 손님이 1분 만에 마음이 바뀌어 "저 그냥 나갈게요!" 하고 문을 박차고 나가버렸습니다. 손님은 없는데 주방장이 불판 위에서 계속 볶음밥을 만들고 있다면 이는 재료 낭비, 불 낭비, 인력 낭비입니다.

웹 서비스도 마찬가지입니다. 유저가 스마트폰에서 '데이터 다운로드'를 눌렀다가 로딩이 1초 이상 걸려서 뒤로 가기를 누르거나 앱을 껐습니다. 하지만 멍청한 백엔드 서버는 유저가 사라진 줄도 모르고 10초 동안 열심히 데이터베이스를 뒤져서 데이터를 가져오는 헛수고를 합니다.

이 끔찍한 자원 낭비를 막아주는 것이 바로 `context.Context`입니다.

Context의 작동 원리

서버에 요청이 들어오면, Go 언어는 그 요청과 연결된 얇은 실(Context)을 하나 만듭니다. 서버는 데이터베이스에 쿼리를 날릴 때나 외부 API를 찌를 때 항상 이 실을 손에 쥐고 들어갑니다. 만약 유저가 뒤로 가기를 눌러서 연결을 끊어버리면, 이 얇은 실이 톡 하고 끊어집니다. 그 순간 서버 깊숙한 곳에서 DB를 뒤지던 코드들이 일제히 "앗! 주인이 나갔다! 일 당장 멈춰!"라고 소리치며 하던 작업을 즉각 취소(Cancel)합니다.

모든 곳에 따라다니는 ctx

바이브코딩으로 짠 백엔드 코드를 보면 함수들의 첫 번째 매개변수 자리에 무조건 `ctx context.Context`가 떡하니 자리 잡고 있는 것을 볼 수 있습니다. 처음엔 왜 이 쓸데없는 걸 계속 전달하나 싶지만, 이것이 바로 언제든 작업을 멈출 수 있는 취소 리모컨입니다.

// 함수 괄호 안에 항상 첫 번째로 등장하는 ctx!
func GetUserPosts(ctx context.Context, userID int) ([]Post, error) {
    // DB를 조회할 때도 이 리모컨(ctx)을 꼭 같이 넘겨주어야 합니다.
    return db.Query(ctx, "SELECT * FROM posts WHERE ...")
}

AI에게 백엔드 코드를 짜달라고 할 때, 반드시 "DB나 외부 통신을 하는 모든 함수는 첫 번째 파라미터로 context.Context를 받아서 제대로 전달하게 뼈대를 짜줘"라고 지시해야 수만 명이 몰려도 끄떡없는 건강한 서버를 만들 수 있습니다.

개념 퀴즈

퀴즈 답을 맞춰야 학습 완료가 됩니다.

사용자가 웹 브라우저에서 '무거운 리포트 생성' 버튼을 눌렀다가, 로딩이 길어지자 짜증이 나서 브라우저 창을 닫아버렸습니다. 백엔드 서버가 브라우저 창이 닫혔다는 신호를 즉시 감지하고, 하던 무거운 작업을 중간에 강제로 멈춰서 서버의 자원(CPU, 메모리) 낭비를 막아주는 Go 언어의 필수 마법 도구는 무엇인가요?

3줄 요약

  1. 1Go 언어의 고루틴(Goroutine)은 함수 앞에 'go' 두 글자만 붙이면 깃털처럼 가벼운 백그라운드 일꾼을 복제하여 엄청난 양의 동시 접속을 처리하게 해줍니다.
  2. 2사용자가 브라우저를 닫거나 연결을 끊었을 때 서버가 헛수고하지 않고 작업을 즉각 중단하려면 context.Context라는 취소 리모컨이 필요합니다.
  3. 3AI 코딩 시 DB 접근이나 외부 네트워크 통신을 하는 모든 로직에는 반드시 ctx 를 전달하도록 지시하여 서버의 자원을 보호해야 합니다.