Skip to content
76 changes: 76 additions & 0 deletions docs/basics/client.ko.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# 클라이언트(Client)

Vapor의 클라이언트는 외부 리소스에 HTTP 요청을 보낼 수 있습니다. 클라이언트는 [async-http-client](https://github.com/swift-server/async-http-client)을 기반으로 만들어졌으며, [content](content.md) API로 통합되어 있습니다.

## 개요

`Application`을 통해 기본 클라이언트에 접근할 수 있습니다. 또는, 라우터 핸들러 안에서 `Request`를 통해 접근할 수 있습니다.

```swift
app.client // Client

app.get("test") { req in
req.client // Client
}
```

애플리케이션의 클라이언트는 설정(Configuration)을 하는 동안에 HTTP 요청을 만드는데 유용합니다. 만약 라우터 핸들러 안에서 HTTP 요청을 만든다면, 항상 request의 클라이언트를 사용하세요.

### 메서드(Methods)

원하는 URL을 `GET` 메서드에 전달해서 `GET` 요청을 만들어보세요.

```swift
let response = try await req.client.get("https://httpbin.org/status/200")
```

`get`, `post`, 그리고 `delete` 같은 각각의 HTTP 메서드를 위한 메서드들이 있습니다. 클라이언트의 응답은 HTTP 상태 코드, 헤더, 본문을 포함하고, Future 형태로 반환됩니다.

### 컨텐츠(Content)

클라이언트의 요청과 응답에서 데이터를 처리하는 데 Vapor의 [content](content.md) API를 사용할 수 있습니다. 컨텐츠를 인코딩하거나, 쿼리 파라미터나 헤더를 요청에 추가하기 위해서는 `beforeSend` 클로저를 사용하세요.

```swift
let response = try await req.client.post("https://httpbin.org/status/200") { req in
// Encode query string to the request URL.
try req.query.encode(["q": "test"])

// Encode JSON to the request body.
try req.content.encode(["hello": "world"])

// Add auth header to the request
let auth = BasicAuthorization(username: "something", password: "somethingelse")
req.headers.basicAuthorization = auth
}
// Handle the response.
```

비슷한 방식으로, `Content`를 사용해서 응답 본문을 디코딩 할 수 있습니다.

```swift
let response = try await req.client.get("https://httpbin.org/json")
let json = try response.content.decode(MyJSONResponse.self)
```

만약 future을 사용한다면, `flatMapThrowing`을 사용할 수 있습니다.

```swift
return req.client.get("https://httpbin.org/json").flatMapThrowing { res in
try res.content.decode(MyJSONResponse.self)
}.flatMap { json in
// Use JSON here
}
```

## 설정(Configuration)

애플리케이션을 통해 내부 HTTP 클라이언트를 설정할 수 있습니다.

```swift
// Disable automatic redirect following.
app.http.client.configuration.redirectConfiguration = .disallow
```

기본 클라이언트는 반드시 처음 _사용하기 전에_ 먼저 설정을 해야 합니다.


70 changes: 70 additions & 0 deletions docs/basics/controllers.ko.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# 컨트롤러(Controllers)

컨트롤러는 코드를 그룹화하는데 좋은 방법입니다. 컨트롤러는 요청(request)을 받고 응답(response)을 반환하는 메서드들의 모음입니다.

컨트롤러가 위치하기에 좋은 곳은 [Controllers](../getting-started/folder-structure.md#controllers) 폴더입니다.

## 개요

예시 컨트롤러를 살펴보겠습니다.

```swift
import Vapor

struct TodosController: RouteCollection {
func boot(routes: RoutesBuilder) throws {
let todos = routes.grouped("todos")
todos.get(use: index)
todos.post(use: create)

todos.group(":id") { todo in
todo.get(use: show)
todo.put(use: update)
todo.delete(use: delete)
}
}

func index(req: Request) async throws -> [Todo] {
try await Todo.query(on: req.db).all()
}

func create(req: Request) async throws -> Todo {
let todo = try req.content.decode(Todo.self)
try await todo.save(on: req.db)
return todo
}

func show(req: Request) async throws -> Todo {
guard let todo = try await Todo.find(req.parameters.get("id"), on: req.db) else {
throw Abort(.notFound)
}
return todo
}

func update(req: Request) async throws -> Todo {
guard let todo = try await Todo.find(req.parameters.get("id"), on: req.db) else {
throw Abort(.notFound)
}
let updatedTodo = try req.content.decode(Todo.self)
todo.title = updatedTodo.title
try await todo.save(on: req.db)
return todo
}

func delete(req: Request) async throws -> HTTPStatus {
guard let todo = try await Todo.find(req.parameters.get("id"), on: req.db) else {
throw Abort(.notFound)
}
try await todo.delete(on: req.db)
return .ok
}
}
```

컨트롤러 메서드는 `Request`을 받고 `ResponseEncodable` 을 준수하는 응답을 반환해야 합니다. 동기 또는 비동기 방식 모두 가능합니다.

마지막으로 `routes.swift`에서 컨트롤러를 등록해야 합니다.

```swift
try app.register(collection: TodosController())
```
144 changes: 144 additions & 0 deletions docs/basics/environment.ko.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# 환경설정(Environment)

Vapor의 환경설정 API는 앱을 동적으로 설정할 수 있도록 합니다. 앱은 기본적으로 `development` 환경을 사용합니다. `production`이나 `staging` 같은 유용한 다른 환경을 정의할 수 있고, 각 케이스마다 다르게 설정할 수 있습니다. 필요에 따라 프로세스의 환경이나 `.env` 파일의 변수들을 사용할 수 있습니다.

`app.environment`를 사용해서 현재 환경설정에 접근할 수 있습니다. `configure(_:)` 메서드 안에서 이 프로퍼티와 스위치를 사용하면 환경에 따라 다르게 설정할 수 있습니다.

```swift
switch app.environment {
case .production:
app.databases.use(....)
default:
app.databases.use(...)
}
```

## 환경설정 변경하기

기본으로 앱은 `development` 모드로 실행됩니다. 앱을 부팅할 때, `--env` (`-e`) 플래그를 전달해서 환경설정을 변경할 수 있습니다.

```swift
swift run App serve --env production
```

Vapor는 다음과 같은 환경이 있습니다.

|name|short|description|
|-|-|-|
|production|prod|사용자들에게 제공하기 위한 환경입니다|
|development|dev|로컬 개발을 위한 환경입니다.|
|testing|test|유닛 테스트를 위한 환경입니다.|

!!! info
특별한 설정이 없다면 `production` 환경은 기본으로 `notice` 레벨로 로그를 기록합니다. 다른 환경들은 기본으로 `info` 레벨입니다.

`--env` (`-e`) 플래그에 전체 또는 축약 이름을 전달해서 설정할 수 있습니다.

```swift
swift run App serve -e prod
```

## 프로세스 변수(Process Variables)

`Environment`는 프로세스의 환경 변수들에 접근할 수 있도록 간단한 문자열 기반의 API를 제공합니다.

```swift
let foo = Environment.get("FOO")
print(foo) // String?
```

`get` 이외에도, `Environment`는 `process`를 통한 동적 멤버 조회 API도 제공합니다.

```swift
let foo = Environment.process.FOO
print(foo) // String?
```

터미널에서 앱을 실행하는 경우에는 `export`를 사용해서 환경 변수를 설정할 수 있습니다.

```sh
export FOO=BAR
swift run App serve
```

Xcode에서 앱을 실행하는 경우에는 `App` Scheme을 수정해서 환경 변수를 설정할 수 있습니다

## .env (dotenv) 파일

Dotenv 파일은 환경 변수를 자동으로 저장하기 위해서 Key-Value 형태의 리스트를 사용합니다. 이 파일은 수동으로 환경 변수를 설정할 필요가 없어서 쉽게 사용할 수 있습니다.

Vapor는 현재 작업 디렉토리에서 dotenv 파일을 검색합니다. 만약 Xcode를 사용한다면 `App` Scheme에서 작업 디렉토리를 지정해 줄 필요가 있습니다.

프로젝트 루트 폴더에 위치한 env 파일에 다음과 같은 내용이 있다고 가정하겠습니다.

```sh
FOO=BAR
```

어플리케이션이 부팅될 때, 다른 프로세스 환경 변수처럼 파일의 컨텐츠에 접근할 수 있습니다.

```swift
let foo = Environment.get("FOO")
print(foo) // String?
```

!!! info
이미 프로세스 환경설정에 존재하는 변수들은 `.env` 파일에 명시되었더라도 재설정되지 않습니다.

Vapor는 `.env` 파일에 외에도 현재 환경을 위한 dotenv 파일을 로드하려고 합니다. 예를 들어 `development` 환경에서는 Vapor는 `.env.development` 파일을 로드할 것입니다. 특정 환경의 `.env` 파일 안의 값들은 일반 `.env` 파일보다 우선시 됩니다.

기본 값으로 구성된 템플릿으로서 `.env` 파일을 프로젝트에 포함하는 것은 일반적인 방식입니다. 특정 환경 파일은 `.gitignore`에서 다음 패턴을 사용해서 업로드되지 않도록 합니다.

```gitignore
.env.*
```

새로운 컴퓨터에 프로젝트를 클론(clone) 할 때, `.env` 템플릿 파일은 복사되고 올바른 값들을 입력할 수 있습니다.

```sh
cp .env .env.development
vim .env.development
```

!!! warning
패스워드 같은 민감한 정보의 dotenv 파일들은 버전 관리에 절대로 커밋(commit) 되지 않도록 하세요.

만약 dotfile를 로드하는데 어려움이 있다면, 더 많은 정보를 위해서 `--log debug` 플래그를 사용해서 디버그 로깅을 시도해 보세요.

## 커스텀 환경설정

`Environment`을 확장해서 커스텀 환경설정 이름을 정의할 수 있습니다.

```swift
extension Environment {
static var staging: Environment {
.custom(name: "staging")
}
}
```

어플리케이션의 환경설정은 보통 `entrypoint.swift`에서 `Environment.detect()`를 사용해서 설정합니다.

```swift
@main
enum Entrypoint {
static func main() async throws {
var env = try Environment.detect()
try LoggingSystem.bootstrap(from: &env)

let app = Application(env)
defer { app.shutdown() }

try await configure(app)
try await app.runFromAsyncMainEntrypoint()
}
}
```

`detect` 메서드는 프로세스의 명령 줄 인자와 `--env` 플래그를 자동으로 사용합니다. 커스텀 `Environment` 구조체를 초기화하는 작업으로 이를 재설정할 수 있습니다.

```swift
let env = Environment(name: "testing", arguments: ["vapor"])
```

arguments 배열에는 최소한 하나의 실행 가능한 이름을 나타내는 인자를 포함해야 합니다. 명령 줄에 인자를 제공하는 것 같은 시뮬레이션을 하기 위해서, 추가적인 인자들을 제공할 수 있습니다. 특히 테스트를 위해서 사용할 때 유용합니다.
Loading