

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# 예제 4: 레시피에 흐름 제어 추가
<a name="cookbooks-101-basics-ruby"></a>

**중요**  
이 AWS OpsWorks Stacks 서비스는 2024년 5월 26일에 서비스 수명이 종료되었으며 신규 및 기존 고객 모두에서 비활성화되었습니다. 가능한 한 빨리 워크로드를 다른 솔루션으로 마이그레이션하는 것이 좋습니다. 마이그레이션에 대한 질문이 있는 경우 [AWS re:Post](https://repost.aws/) 또는 [AWS Premium Support](https://aws.amazon.com/support)를 통해 AWS Support 팀에 문의하세요.

일부 레시피는 일련의 Chef 리소스에 불과합니다. 이 경우, 레시피를 실행하면 레시피는 단순히 각각의 리소스 공급자를 순차적으로 실행합니다. 하지만 보다 복잡한 실행 경로가 있으면 유용한 경우가 많습니다. 다음은 두 가지 일반적인 시나리오입니다.
+ 레시피가 같은 리소스를 서로 다른 속성 설정으로 여러 번 실행하도록 하려는 경우.
+ 운영 체제에 따라 서로 다른 속성 설정을 사용하려는 경우.

이러한 시나리오는 Ruby 제어 구조를 레시피에 통합하여 해결할 수 있습니다. 이 섹션에서는 [예제 3: 디렉터리 생성](cookbooks-101-basics-directories.md)의 레시피를 수정하여 두 가지 시나리오를 모두 해결하는 방법을 살펴봅니다.

**Topics**
+ [반복](#cookbooks-101-basics-ruby-iteration)
+ [조건부 논리](#cookbooks-101-basics-ruby-conditional)

## 반복
<a name="cookbooks-101-basics-ruby-iteration"></a>

[예제 3: 디렉터리 생성](cookbooks-101-basics-directories.md)에서는 `directory` 리소스를 사용하여 디렉터리 또는 디렉터리 체인을 생성하는 방법을 살펴봤습니다. 하지만 `/srv/www/config`와 `/srv/www/shared`라는 2개의 별도의 디렉터리를 생성하려 한다고 가정해 봅시다. 각 디렉터리마다 별도의 디렉터리 리소스를 구성할 수도 있지만 아주 많은 디렉터리를 생성하려는 경우, 이 방법은 번거로울 수 있습니다. 다음 레시피는 이 작업을 처리하는 보다 간단한 방법을 보여 줍니다.

```
[ "/srv/www/config", "/srv/www/shared" ].each do |path|
  directory path do
    mode 0755
    owner 'root'
    group 'root'
    recursive true
    action :create
  end
end
```

이 레시피는 각 하위 디렉터리에 별도의 디렉터리 리소스를 사용하는 대신 하위 디렉터리 경로가 포함된 문자열 모음을 사용합니다. Ruby `each` 메서드는 첫 번째 모음 요소부터 시작하여 각 모음 요소에 한 번씩 리소스를 실행합니다. 요소의 값은 리소스에서 `path` 변수에 의해 표시되며, 이 경우에는 디렉터리 경로를 나타냅니다. 이 예제를 쉽게 조정하여 얼마든지 많은 하위 디렉터리를 생성할 수 있습니다.

**레시피를 실행하려면**

1. `createdir` 디렉터리를 그대로 열어 두고, 다음 여러 예제에 이 쿡북을 사용합니다.

1. 아직 실행하지 않은 경우 `kitchen destroy`를 실행합니다. 그러면 깨끗한 인스턴스로 시작할 수 있습니다.

1. `default.rb`의 코드를 이 예제로 바꾸고 `kitchen converge`를 실행합니다.

1. 인스턴스에 로그인합니다. 그러면 `/srv`에 새로 생성된 디렉터리가 있습니다.

해시 테이블을 사용하여 각 반복에 2개의 값을 지정할 수 있습니다. 다음 레시피는 각각 다른 모드로 `/srv/www/config`와 `/srv/www/shared`를 생성합니다.

```
{ "/srv/www/config" => 0644, "/srv/www/shared" => 0755 }.each do |path, mode_value|
  directory path do
    mode mode_value
    owner 'root'
    group 'root'
    recursive true
    action :create
  end
end
```

**레시피를 실행하려면**

1. 아직 실행하지 않은 경우 `kitchen destroy`를 실행합니다. 그러면 깨끗한 인스턴스로 시작할 수 있습니다.

1. `default.rb`의 코드를 이 예제로 바꾸고 `kitchen converge`를 실행합니다.

1. 인스턴스에 로그인합니다. 그러면 `/srv`에 지정된 모드로 새로 생성된 디렉터리가 있습니다.

**참고**  
OpsWorks Stacks 레시피는 일반적으로이 접근 방식을 사용하여 기본적으로 큰 해시 테이블인 [스택 구성 및 배포 JSON](workingcookbook-json.md)에서 값을 추출하고 리소스에 삽입합니다. 예제는 [Deploy 레시피](create-custom-deploy.md) 섹션을 참조하세요.

## 조건부 논리
<a name="cookbooks-101-basics-ruby-conditional"></a>

Ruby 조건부 논리를 사용하여 여러 실행 분기를 생성할 수도 있습니다. 다음 레시피는 `if-elsif-else` 로직을 사용하여 이전 예제를 확장하므로 `/srv/www/shared`라는 이름의 하위 디렉터리를 생성하지만 Debian 및 Ubuntu 시스템에서만 가능합니다. 다른 모든 시스템의 경우, Test Kitchen 출력에 표시되는 오류 메시지를 로깅합니다.

```
if platform?("debian", "ubuntu")
  directory "/srv/www/shared" do
    mode 0755
    owner 'root'
    group 'root'
    recursive true
    action :create
  end
else
  log "Unsupported system"
end
```

**예제 레시피를 실행하려면**

1. 인스턴스가 여전히 실행 중인 경우 `kitchen destroy`를 실행하여 종료합니다.

1. `default.rb`의 코드를 이 예제 코드로 바꿉니다.

1. `.kitchen.yml`을 편집하여 CentOS 6.4 시스템을 플랫폼 목록에 추가합니다. 파일의 `platforms` 섹션은 다음과 같아야 합니다.

   ```
   ...
   platforms:
     - name: ubuntu-12.04
     - name: centos-6.4
   ...
   ```

1. `kitchen converge`를 실행합니다. 이 코드는 인스턴스를 생성하고 각 플랫폼에 대해 `.kitchen.yml`의 레시피를 순서대로 실행합니다.
**참고**  
인스턴스를 하나만 수렴하려는 경우 인스턴스 이름을 파라미터로 추가합니다. 예를 들어, Ubuntu 플랫폼에서만 레시피를 수렴하려면 `kitchen converge default-ubuntu-1204`를 실행합니다. 플랫폼 이름을 모르는 경우에는 `kitchen list`를 실행하면 됩니다.

Test Kitchen 출력의 CentOS 부분에 다음과 같은 로그 메시지가 표시되어야 합니다.

```
...
Converging 1 resources
Recipe: createdir::default
* log[Unsupported system] action write[2014-06-23T19:10:30+00:00] INFO: Processing log[Unsupported system] action write (createdir::default line 12)
[2014-06-23T19:10:30+00:00] INFO: Unsupported system
       
[2014-06-23T19:10:30+00:00] INFO: Chef Run complete in 0.004972162 seconds
```

이제 인스턴스에 로그인하여 디렉터리가 생성되었는지 여부를 확인할 수 있습니다. 다만 지금은 `kitchen login`을 실행할 수 없습니다. 플랫폼 이름(예: `kitchen login default-ubuntu-1204`)을 추가하여 어떤 인스턴스인지 지정해야 합니다.

**참고**  
Test Kitchen 명령이 인스턴스 이름을 받는 경우, 완전한 이름을 입력할 필요가 없습니다. Test Kitchen은 인스턴스 이름을 Ruby 정규식으로 취급하므로 고유한 일치를 제공하기에 충분한 문자만 있으면 됩니다. 예를 들어 `kitchen converge ub`를 실행하여 Ubuntu 인스턴스만 수렴하거나 `kitchen login 64`를 실행하여 CentOS 인스턴스에 로그인할 수 있습니다.

아마도 지금 시점에서 의문은 어떻게 레시피가 어느 플랫폼에서 실행 중인지 아느냐일 것입니다. Chef는 플랫폼을 포함한 시스템 데이터를 수집하는 모든 실행에 대해 [Ohai](https://docs.chef.io/ohai.html)라는 도구를 실행하며, 이를 *노드 객체*라고 하는 구조에서 속성 집합으로 나타냅니다. Chef `platform?` 메서드는 괄호로 묶인 시스템들을 Ohai 플랫폼 값과 비교하고 그중 하나가 일치하면 true를 반환합니다.

`node['attribute_name']`을 사용하면 코드에서 직접 노드 속성의 값 단원을 참조할 수 있습니다. 예를 들어 플랫폼 값은 `node['platform']`으로 나타냅니다. 가령 이전 예제를 다음과 같이 작성했을 수 있습니다.

```
if node[:platform] == 'debian' or node[:platform] == 'ubuntu'
  directory "/srv/www/shared" do
    mode 0755
    owner 'root'
    group 'root'
    recursive true
    action :create
  end
else
  log "Unsupported system"
end
```

레시피에 조건부 논리를 포함시키는 일반적 이유는 Linux 제품군마다 가끔 패키지, 디렉터리 등에 사용되는 이름이 다르다는 점을 감안하기 위해서입니다. 예를 들어 Apache 패키지 이름은 CentOS 시스템에서는 `httpd`이고 Ubuntu 시스템에서는 `apache2`입니다.

시스템에 따라 다른 문자열만이 필요한 경우, Chef [http://docs.chef.io/dsl_recipe.html#value-for-platform](http://docs.chef.io/dsl_recipe.html#value-for-platform) 메서드가 `if-elsif-else`보다 간단한 해법입니다. 다음 레시피는 CentOS 시스템에 `/srv/www/shared` 디렉터리, Ubuntu 시스템에 `/srv/www/data` 디렉터리, 그 밖의 모든 시스템에 `/srv/www/config` 디렉터리를 생성합니다.

```
data_dir = value_for_platform(
  "centos" => { "default" => "/srv/www/shared" },
  "ubuntu" => { "default" => "/srv/www/data" },
  "default" => "/srv/www/config"
)
directory data_dir do
  mode 0755
  owner 'root'
  group 'root'
  recursive true
  action :create
end
```

`value_for_platform`은 `data_dir`에 적절한 경로를 할당하며, `directory` 리소스는 이 값을 사용하여 디렉터리를 생성합니다.

**예제 레시피를 실행하려면**

1. 인스턴스가 여전히 실행 중인 경우 `kitchen destroy`를 실행하여 종료합니다.

1. `default.rb`의 코드를 이 예제 코드로 바꿉니다.

1. `kitchen converge`를 실행한 다음 각 인스턴스에 로그인하여 해당 디렉터리가 있는지 확인합니다.