Menu
Coddy logo textTech

데코레이터로서의 Middleware

Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 107개 중 97번째.

Middleware는 Go에서 Decorator 패턴을 가장 실용적으로 적용한 사례 중 하나입니다. 웹 개발에서 미들웨어 함수는 HTTP 핸들러를 감싸 원래 핸들러를 수정하지 않고 로깅, 인증 또는 실행 시간 측정과 같은 횡단 관심사를 추가합니다.

핵심 통찰은 middleware가 handler를 받아 동작이 추가된 새로운 handler를 반환한다는 것입니다. 이는 Decorator 패턴을 정확히 따릅니다. 입력과 출력에서 동일한 인터페이스를 사용하면서 기능이 향상됩니다.

type Handler func(request string) string

func LoggingMiddleware(next Handler) Handler {
    return func(request string) string {
        result := next(request)
        return "[LOG] " + result
    }
}

func AuthMiddleware(next Handler) Handler {
    return func(request string) string {
        return "[AUTH] " + next(request)
    }
}

각 middleware는 체인의 next handler를 감싸며, 감싼 handler를 호출하기 전이나 후에 해당 동작을 추가합니다. 여러 middleware를 조합하여 쌓을 수 있습니다.

func MainHandler(request string) string {
    return "Response to: " + request
}

handler := LoggingMiddleware(AuthMiddleware(MainHandler))
fmt.Println(handler("GET /users"))
// [LOG] [AUTH] 응답: GET /users

래핑의 order가 실행 순서를 결정합니다. outermost middleware가 first 실행됩니다. 이 패턴은 Go 웹 프레임워크에서 request 처리 pipeline에 광범위하게 사용되므로, 핵심 handler 로직을 건드리지 않고 기능을 추가하거나 제거하기 쉽습니다.

challenge icon

챌린지

쉬움

Middleware as Decorator 패턴을 사용하여 request processing pipeline을 만들어 봅시다! timing, validation, formatting과 같은 기능을 추가하기 위해 middleware function이 handler를 감싸는 시스템을 만들 것입니다. 이 모든 작업은 핵심 handler 로직을 수정하지 않고 수행됩니다.

코드를 세 개의 파일로 구성합니다.

  • handler.go: handler type과 request를 process하는 base handler를 정의합니다.

    request string을 받아 response string을 반환하는 Handler type을 function으로 만듭니다.

    type Handler func(request string) string

    받은 모든 request에 대해 Processed: [request]를 반환하는 BaseHandler function을 Implement합니다.

  • middleware.go: 추가 동작으로 handler를 감싸는 middleware function을 만듭니다.

    각각 Handler를 받아 새로운 Handler를 반환하는 세 개의 middleware function을 Build합니다.

    • TimingMiddleware: response를 timing 정보로 감싸며 [TIMING] [response]를 반환합니다.
    • ValidationMiddleware: validation prefix를 추가하며 [VALID] [response]를 반환합니다.
    • UppercaseMiddleware: entire response를 대문자로 converts합니다.

    각 middleware는 감싸진 handler를 Call하고 그 결과를 향상해야 합니다.

  • main.go: middleware를 composed하여 processing pipeline을 Build합니다.

    Apply할 middleware layers의 number를 read합니다. 그런 다음 각 layer에 대해 middleware type(timing, validation 또는 uppercase)을 read하고 현재 handler를 해당 middleware로 감쌉니다. 읽은 order로 Apply합니다. 처음 read한 middleware가 outermost wrapper가 됩니다.

    pipeline을 Build한 후 request string을 read하고, composed handler를 통해 process한 다음 결과를 print합니다.

다음 input이 제공됩니다.

  • Line 1: middleware layers의 Number
  • Following lines: 각 line에 하나씩 middleware type(timing, validation 또는 uppercase)
  • Last line: process할 request

예를 들어 다음과 같은 경우:

2
timing
validation
GET /users

Output은 다음과 같아야 합니다.

[TIMING] [VALID] Processed: GET /users

그리고 다음과 같은 경우:

3
uppercase
timing
validation
POST /data

Output은 다음과 같아야 합니다.

[TIMING] [VALID] PROCESSED: POST /DATA

또한 다음과 같은 경우:

1
uppercase
hello world

Output은 다음과 같아야 합니다.

PROCESSED: HELLO WORLD

그리고 다음과 같은 경우:

0
simple request

Output은 다음과 같아야 합니다.

Processed: simple request

middleware wrapping order가 Output에 어떤 영향을 주는지 확인하세요. 처음 Apply된 middleware(outermost)가 먼저 실행되고, 모든 inner middleware를 거친 final result를 확인합니다. 두 번째 예시의 uppercase middleware는 last(innermost)에 실행되므로, timing과 validation이 prefix를 adds하기 전에 base response를 대문자로 converts합니다!

REQUIRED OUTPUT FORMAT: [Your translated content here]

직접 해보기

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// 미들웨어 레이어 수 읽기
	nLine, _ := reader.ReadString('\n')
	n, _ := strconv.Atoi(strings.TrimSpace(nLine))

	// 미들웨어 타입 읽기
	middlewareTypes := make([]string, n)
	for i := 0; i < n; i++ {
		line, _ := reader.ReadString('\n')
		middlewareTypes[i] = strings.TrimSpace(line)
	}

	// 요청 읽기
	request, _ := reader.ReadString('\n')
	request = strings.TrimSpace(request)

	// TODO: BaseHandler로 시작

	// TODO: 핸들러를 미들웨어로 래핑하여 파이프라인 구축
	// 순서대로 미들웨어 적용 - 먼저 읽은 미들웨어가 가장 바깥쪽 래퍼가 됨
	// Hint: 이를 위해 middlewareTypes를 역순으로 순회
	// 마지막으로 적용된 미들웨어가 가장 바깥쪽(먼저 실행됨)

	// TODO: 구성된 핸들러를 통해 요청을 처리하고 결과 출력
	fmt.Println("TODO: implement pipeline")
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 Go 컴파일러