도커 컴포즈를 활용하여 완벽한 개발 환경 구성하기
아직 도커 Docker 를 잘 모르는 분께는 도커 튜토리얼: 깐 김에 배포까지를 추천합니다. 도커를 왜 써야할지 설득이 필요한 분께는 왜 굳이 도커를 써야 하나요? 글을 추천합니다.
그럼, 바로 본론으로 넘어갑니다.
1 개발 환경 구성이라는 어려움
새 프로젝트를 시작하거나 기존 프로젝트에 참여한 후 가장 먼저 하는 일은 바로, 개발 환경 구성일 겁니다. 파이썬 Python 을 사용하는 프로젝트라면 보통 pip 명령과 requirements.txt 파일을 사용해 의존 패키지를 설치합니다.
거의 언제나 하나 이상의 프로젝트를 진행해야 하니까, 의존성이 섞이지 않게 virtualenv도 사용합니다.
이렇게 프로젝트별로 의존성을 관리하더라도 문제가 없는 것은 아닙니다. 의존성 패키지를 설치하는 과정이 무사히 성공하면 좋겠지만 실제로는 다음 상황을 거의 항상 겪기 마련입니다.
- 패키지 설치가 막힌다(SSL, LDAP 관련).
- (실수가 있건 없건) 로컬 컴퓨터에 이미 존재하는 환경과 꼬였다.
- 막상 배포를 했더니 개발 환경과 서버 환경이 같지 않아서 문제가 발생했다.
보안 관련 패키지에서는 이런 식의 오류를 만납니다.
데이터베이스와 관련해서는 이런 오류도 만나고요.
이미지 관련 패키지는 이런 오류를 선사합니다.
어떤 패키지는 sudo 권한을 달라고도 하고요.
물론 설치가 잘 끝났어도 이런 오류를 만날 수 있죠.
설치야 어차피 한 번만 하고 마는 것 아닌가? 하고 생각하실 수도 있겠지만, 프로젝트에 새로운 사람이 합류하면 내가 겪었을 어려움 혹은 그 사람의 개발 환경에 따른 또다른 어려움들을 겪게 됩니다. 문서화를 잘 해두었다면 조금은 수월할 수도 있겠지만, 이미 개발 환경을 구성한 사람이라면 굳이 개발 환경 구성에 시간을 더 들일 필요가 없으니 가이드 문서는 금새 낡아버립니다. 게다가 설치 과정 외에도 신경 써야 할 요소(환경 변수, 데이터베이스 인코딩 등)가 항상 발목을 잡기 마련입니다.
개발 환경 구성에 대한 문서를 계속 업데이트한다면 이런 문제가 최소화되겠지만, 상상하는 것만으로도 진이 빠지는 일인 건 분명합니다.
2 도커로 개발 환경 구성하기
도커를 통해 여러 환경에 동일한 서버를 배포할 수 있다는 점에 주목하면, 개발 환경도 어디서나 동일하게 구성할 수 있겠다고 상상할 수 4 우디의 선택을 이해하기까지 - CoAR(코아르) 있습니다. 먼저, 간단한 웹 애플리케이션의 개발 환경을 도커로 구성해보고 불편한 점을 살펴본 후, 이를 편하게 만들어 주는 도커 컴포즈 Docker Compose 로 넘어가겠습니다. *
* 도커에 익숙한 분이라면 도커 컴포즈로 개발 환경 구성하기로 건너뛰어도 좋습니다.
먼저 도커 컴포즈 용 예시 프로젝트 저장소를 클론합니다.
2.1 도커 이미지 빌드
이제 프로젝트의 루트 디렉터리에서 도커 이미지를 만듭니다.
이렇게 하면 docker-sample 이라는 도커 이미지가 만들어집니다.
2.2 Django 앱 컨테이너 실행
이제 Django 앱 컨테이너를 실행해봅시다. 다음 명령은 django-sample 이미지를 컨테이너로 실행합니다.
데이터베이스가 연결되지 않았기 때문에, 마지막 부분에 이런 오류가 나타날 겁니다.
2.3 데이터베이스 컨테이너 실행
물론 데이터베이스 서버도 도커로 실행할 수 있습니다. 터미널 창을 하나 더 열고 다음 명령을 입력해보세요.
데이터베이스 서버가 실행되었지만, 앱 개발 서버는 여전히 동작하지 않을 겁니다. 일단 두 컨테이너의 터미널 창에서 Ctrl + C 키를 눌러 컨테이너를 종료하고, 이 문제를 해결해봅시다.
2.4 데이터베이스 연결하기
기본적으로 도커 컨테이너들은 각각 격리된 환경에서 실행됩니다. 다시 말해, 4 우디의 선택을 이해하기까지 - CoAR(코아르) 별도의 옵션을 지정하지 않으면 다른 컨테이너의 존재를 알 수 없죠. 앞에서는 앱 컨테이너에 별 옵션을 지정하지 않고 실행한 후, 데이터베이스 컨테이너를 실행했습니다. 따라서 앱 컨테이너는 PostgreSQL 컨테이너가 실행되었는지 여부를 알지 못합니다.
앱 컨테이너에게 데이터베이스 컨테이너의 존재를 알려주기 위해서는 다음 과정을 거쳐야 합니다.
- 데이터베이스 컨테이너를 실행하면서 컨테이너 4 우디의 선택을 이해하기까지 - CoAR(코아르) 4 우디의 선택을 이해하기까지 - CoAR(코아르) 이름을 붙이고( db 라고 하죠),
- 앱 컨테이너를 실행할 때 db 컨테이너를 연결해 줍니다.
먼저 데이터베이스 컨테이너를 실행해보죠.
- 이번에는 -it 옵션을 삭제하여, 컨테이너를 데몬 형태로 실행했습니다.
- --name : 데이터베이스 컨테이너에 db 라는 이름을 붙였습니다.
- -e : 환경변수를 설정하여 컨테이너를 실행할 수 있습니다. 여기서는 컨테이너 시작과 함께 djangosample 데이터베이스를 만들고( POSTGRES_DB ) sampleuser 라는 사용자에게 접속 권한을 부여( POSTGRES_USER , POSTGRES_PASSWORD )합니다. *
* 도커 허브의 공식 포스트그레SQL 저장소에 가보면 설정할 수 있는 환경 변수 종류를 알 수 있습니다.
참고로, 여기서 설정한 사용자 이름이나 비밀번호, 데이터베이스 이름 같은 환경변수들은 djangosample/settings.py 파일 안에 설정해 둔 기본값들입니다.
이제 앱 컨테이너를 실행하면서 db 컨테이너를 연결( link )해 줍시다.
- --link : 참조할 다른 컨테이너를 지정합니다.
이렇게 해서 Django 서버가 잘 실행되었다면 다음과 같은 Django 실행 화면을 볼 수 있습니다.
Django 개발 서버가 잘 실행되었습니다
이제 개발만 하면 될까요? 아쉽게도 그렇지 않습니다.
2.5 코드 변경이 실시간으로 반영되게 하기
프로젝트의 Dockerfile 을 살펴보면 ADD ./djangosample /app/djangosample/ 부분이 존재합니다(10행). 도커 이미지가 만들어지다가 10행을 만나면 컨테이너 밖의 소스 코드가 컨테이너 속에 추가되고, 이 시점부터 컨테이너 밖의 소스 코드 변경에 영향을 받지 않습니다. 그래서, 평소 개발하듯 소스 코드를 아무리 수정하더라도 Django 개발 서버가 이를 반영하여 재시작하는 일이 발생하지 않습니다.
이를 해결하고자 격리된 컨테이너에 통로를 하나 만듭니다. 수정할 소스 코드의 디렉터리를 앱 컨테이너 내부의 소스 코드와 연결하여, 코드를 바꿀 때마다 컨테이너 속 코드도 바뀌게 해보겠습니다. Ctrl + C 키를 눌러 앱 컨테이너를 종료한 후, 다음 명령어를 실행합니다.
- --volume : 이 옵션을 사용하여 로컬 디렉터리의 특정 경로를 컨테이너 내부로 마운트할 수 있습니다. ( $(pwd) 는 이 명령을 실행하는 현재 디렉터리의 절대 경로입니다.)
이제 마지막 문제가 남았습니다. 현재 상태에서 Ctrl + C 키를 눌러 앱 컨테이너를 종료하고, docker stop db 명령을 사용해 데이터베이스 컨테이너도 종료해봅시다. 개발하면서 만들었던 데이터들은 다 어떻게 될까요?
2.6 데이터베이스 데이터를 보존하기
앞에서 데이터베이스 컨테이너를 실행하면서 --rm 옵션을 지정했는데요. 이렇게 하면 데이터베이스 컨테이너가 종료와 함께 삭제되면서 컨테이너 내부에 쌓였던 데이터베이스 데이터들도 모두 사라집니다. 깔끔해서 좋긴 하지만 개발용 데이터를 매번 쌓기가 귀찮은 경우도 있습니다.
데이터베이스가 데이터를 저장할 때 파일 시스템을 사용한다는 점을 생각해보면, 이를 해결할 때도 역시 --volume 옵션을 사용할 수 있습니다. 다음 명령을 살펴봅시다.
- --volume : 데이터베이스 컨테이너 내부의 디렉터리( /var/lib/postgresql/data )를 로컬 컴퓨터의 디렉터리( $(pwd)/docker/data )로 연결하였습니다. 이제 데이터베이스가 데이터를 저장할 파일 시스템으로 로컬 컴퓨터의 $(pwd)/docker/data 디렉터리를 사용합니다.
2.7 순서를 맞춰 컨테이너 재실행하기
이제 컨테이너들을 실행하는 run 명령어는 모두 완성되었으니, 모든 컨테이너를 종료한 후 순서를 맞춰 컨테이너들을 재시작해봅시다.
데이터베이스 컨테이너를 실행하고,
새 터미널 창에서 앱 컨테이너를 실행합니다.
이제 Django 서버가 데이터베이스와 잘 연결되고, 소스 코드를 변경하면 개발 서버가 알아서 재시작하며, 컨테이너를 종료해도 데이터베이스에 쌓인 데이터가 사라지지 않습니다.
그런데 개발에 앞서 매번 이 명령어들을 입력한다고 생각해보면… 그리 유쾌하진 않겠죠?
2.8 도커가 별로 편하진 않네요?
도커로 개발 환경을 구성하는 데 성공하긴 했지만, 몇몇 불편한 부분이 남았습니다.
개발 서버를 실행할 때마다 장황한 도커 명령어의 옵션들을 적기가 귀찮습니다. 헷갈리기도 하고, 빼먹는 경우도 생깁니다. 옵션들을 미리 적어두고 한 번에 짜잔~! 실행하면 좋겠는데 말이죠.
- 앱 컨테이너와 데이터베이스 컨테이너의 실행 순서
반드시 데이터베이스 컨테이너를 실행한 다음에 앱 컨테이너를 실행해야 합니다. 그렇지 않으면 앱 컨테이너에서 데이터베이스 컨테이너를 찾을 수 없기 때문이죠. 깜박하고 앱 컨테이너부터 실행했다면? 종료하고 데이터베이스 컨테이너 실행하고, 다시 앱 컨테이너를 실행하는 불편함이 있죠.
3 도커 컴포즈로 개발 환경 구성하기
실행하기 불편하다는 이유로 도커를 포기하기에는 환경 독립성이 주는 장점이 너무 컸나봅니다. 도커가 출시된지 얼마 지나지 않아 독립된 개발 환경을 빠르게 구성할 수 있는 피그 Fig 프로젝트가 선보였습니다. 이때만 해도 도커 명령을 실행하는 서드파티 같은 도구였으나 피그가 인기를 얻자, 도커에서는 피그 프로젝트를 흡수하여 도커 컴포즈 Docker Compose 라는 이름의 도구로 만들어버립니다.
도커 컴포즈를 사용하면 컨테이너 실행에 필요한 옵션을 docker-compose.yml 이라는 파일에 적어둘 수 있고, 컨테이너 간 실행 순서나 의존성도 관리할 수 있습니다.
3.1 선행 조건
이후의 과정을 따라하려면, 도커 엔진의 버전이 1.13.1 이상이어야 하고, 도커 컴포즈의 버전은 1.6.0 이상이어야 합니다. 최근에 도커를 설치했다면 큰 문제가 없을 겁니다.
이제 docker-compose.yml 파일을 만들어 봅시다.
3.2 docker-compose.yml 파일
docker-compose.yml 은 장황한 도커 실행 옵션을 미리 적어둔 문서라고 볼 수 있습니다. 프로젝트 루트에 파일을 만들고, 다음 내용을 붙여 넣습니다. (gist 링크)
이제 파일의 윗부분부터 한 줄씩 살펴봅시다.
3.2.1 version
docker-compose.yml 파일의 첫 줄에는 파일 규격 버전을 적습니다. 파일의 규격에 따라 지원하는 옵션이 달라지는데, “3”이라만 적으면 3으로 시작하는 최신 버전을 사용한다는 의미입니다. (파일 규격 버전에 따른 자세한 내용은 compose 파일의 버전과 호환성을 안내한 공식 문서를 참고하세요.)
3.2.2 services
이 항목 밑에 실행하려는 컨테이너들을 정의합니다. 컴포즈에서는 컨테이너 대신 서비스라는 개념을 사용합니다.
3.2.3 db
postgres 서비스의 이름을 db 로 정하였습니다.
3.2.4 image
db 서비스에서 사용할 도커 이미지를 적습니다. 여기서는 dockerhub의 공식 postgres 이미지를 사용하였습니다.
3.2.5 volumes
docker run 으로 db 컨테이너를 실행할 때 --volume 옵션을 사용하여 데이터베이스의 데이터를 로컬 컴퓨터에 저장했던 부분과 같습니다. 다만 docker-compose.yml 의 volumes 에는 상대 경로를 지정할 수 있어서 편리합니다.
docker run 으로 db 컨테이너를 실행할 때와 마찬가지로, 프로젝트 루트 아래의 docker/data 디렉터리에 데이터를 저장하기로 했습니다.
3.2.6 environment
docker run 명령어의 -e 옵션에 적었던 내용들입니다. 마지막의 POSTGRES_INITDB_ARGS 부분이 추가되었는데, 데이터베이스 서버의 인코딩을 UTF-8로 설정하기 위함입니다.
3.2.7 django
앱 서비스의 이름은 django 로 지정하였습니다.
3.2.8 build
db 서비스와 달리 앱 서비스는 특정 이미지 대신 build 옵션을 추가합니다.
context 는 docker build 명령을 실행할 디렉터리 경로라고 보시면 됩니다.
dockerfile 에는 ‘개발용’ 도커 이미지를 빌드하는 데 사용할 Dockerfile을 지정하면 됩니다. Dockerfile-dev 파일에서는 (운영용 Dockerfile과는 달리) 소스코드를 컨테이너에 넣지 않습니다.
3.2.9 environment
환경 변수는 docker run 을 할 때보다 좀더 자세하게 적었습니다. 각 값은 앱 서비스의 환경 변수로 설정되며, Django 설정 파일( djangosample/settings.py )에서 불러와 사용하게 됩니다. (데이터베이스 관련 정보들은 db 서비스에서 설정한 값들과 일치해야 합니다.)
3.2.10 ports
docker run 명령어의 -p 옵션에 해당하는 부분입니다.
3.2.11 command
docker run 으로 앱 컨테이너를 실행할 때 가장 마지막에 적었던 명령어 부분입니다.
3.2.12 volumes
docker run 으로 앱 컨테이너를 실행할 때 -v 옵션을 사용하여 프로젝트 루트 디렉터리를 컨테이너 안의 /app 디렉터리와 연결했던 부분과 같습니다.
이렇게 해서 docker-compose.yml 파일을 모두 살펴보았습니다.
3.3 드디어 실행!
docker-compose.yml 을 모두 작성했다면 드디어 서비스를 실행할 차례입니다.
이제 http://127.0.0.1:8000에 접속해보면 개발 서버가 잘 작동함을 확인할 수 있습니다.
각 컨테이너를 실행하던 길고 복잡한 명령어들이 사라졌고, 간단한 명령어 한 줄로 개발 서버와 데이터베이스를 모두 실행하였습니다.
3.3.1 그런데… 앱 서비스에서 db 서비스를 어떻게 찾았지?4 우디의 선택을 이해하기까지 - CoAR(코아르)
사실 docker-compose.yml 에는 docker run 에서 사용하던 옵션 중 하나가 보이지 않습니다. 바로 --link 옵션인데요. 도커 컴포즈 파일 버전이 2일 때는 다음과 같은 항목으로 연결할 서비스를 명시하곤 했습니다.
도커 컴포즈 파일 버전 3으로 와서는 links 항목을 사용하지 않더라도 한 네트워크 안에 있는 서비스끼리 서로 통신을 할 수 있기 때문에, 이 항목을 사용하지 않았습니다. (관련 문서인 Links topic in Networking in Compose도 참고하세요.) 한 네트워크로 선언한 적이 없다고요? 한 docker-compose.yml 안에 있는 서비스들은 별도로 지정하지 않으면 하나의 네트워크에 속합니다. (네트워크와 관련된 더 자세한 내용은 Networking in Compose를 참고하세요.)
3.4 개발용 Dockerfile을 별도로 관리하기
앞에서 개발 서버용 Dockerfile-dev 파일을 짧게 언급하고 넘어갔는데요. 여기서 조금 더 자세히 살펴보겠습니다.
크게 보자면 개발 서버에 필요 없는 내용은 지우고, 개발 서버에만 필요한 내용은 추가하면 됩니다. 먼저 ./compose/django/Dockerfile-dev 파일의 내용을 살펴봅시다.
위에서 주석으로 처리한 부분들은 원래 Dockerfile 에는 있었지만 개발 서버용 도커 이미지에서 삭제한 내용입니다.
- 앱 코드: 앱 코드는 컨테이너 안에 집어 넣지 않고 로컬 컴퓨터의 디렉터리를 참조합니다. ( docker-compose.yml 의 volume 부분)
- CMD : 도커 컴포즈의 command 로 관리하는 편이 더 쉽습니다.
이제 ./compose/django/Dockerfile-dev 의 내용은 다음과 같습니다. (gist 링크)
이렇게만 해도 개발 환경을 구성하고 실행하기가 한결 수월해지지만, 실제로 사용하다보니 소소하게 불편한 점들이 있더군요. 이를 개선해보겠습니다.
3.5 개선할 점 몇 가지
3.5.1 데이터베이스 데이터용 볼륨 추가
지금까지는 데이터베이스의 실제 데이터를 ./docker/data 에 저장하고 있었는데, 실수로 이 디렉터리가 지워진다거나 소스코드 버전 관리 시스템에 들어가버리면 낭패일 겁니다. 이 디렉터리를 직접 관리하지 말고 도커에 맡겨봅시다. docker-compose.yml 파일을 다음과 같이 수정합니다.
처음 바뀐 부분에서는 django_sample_db_dev 라는 이름으로 볼륨을 하나 만듭니다. 이렇게 만들어진 볼륨은 docker volume ls 명령으로 확인할 수 있습니다. (볼륨은 도커가 관리하는 가상 디스크라고 생각하면 됩니다.)
이렇게 만든 볼륨을 db 서비스에서 사용하려면, db 서비스 선언부 안에 volumes 항목을 넣고 - 가상디스크_이름:컨테이너_속_디렉터리 처럼 지정합니다. 이후로는 모든 데이터베이스 데이터가 django_sample_db_dev 볼륨에 저장됩니다. (이 볼륨을 지우려면 docker volume rm django_sample_db_dev 명령을 사용합니다.)
3.5.2 파이썬 로그가 한 발 느리게 출력되는 문제 수정
도커 컴포즈에서 파이썬 로그가 한 발 늦게 출력된다는 느낌을 받을 때가 있습니다. 파이썬에서 출력 버퍼가 기본으로 작동하면서 출력 로그를 붙잡고 있기 때문인데요. 이 버퍼링을 없애려면 PYTHONUNBUFFERED 환경변수를 추가하면 됩니다.
./compose/django/Dockerfile-dev 에 다음 내용을 추가합니다. (설정할 값은 0이든 1이든 상관 없고, 환경변수가 존재하기만 하면 됩니다.)
이렇게 해서 도커 컴포즈로 개발 환경을 어떻게 구성하는지 살펴보았습니다. 이제 도커 컴포즈에서 주로 사용하는 명령어를 간략하게 훑어보겠습니다.
3.5.3 Django 서버가 데이터베이스를 못 찾아요
이는 대부분 첫 실행시에만 발생하는 문제입니다. 데이터베이스 서비스가 실행된 후 초기화되기 전에(대략 5초~10초) Django 서버가 실행되기 때문인데요. 가장 간단한 해결 방법은 서비스를 중지하고 다시 실행하는 것이지만 근본적인 해결책은 아닙니다. 이를 위해 wait-for-it.sh이라는 셸 스크립트를 사용해보겠습니다.
wait-for-it.sh는 이름이 알려주듯 특정 서버의 특정 포트로 접근할 수 있을 때까지 기다려주는 스크립트입니다. 이 스크립트를 도커 이미지 안에 넣고, 이미지 실행 명령 앞에 붙여주면 됩니다.
./compose/django/Dockerfile-dev 파일의 마지막 부분에 다음 내용을 추가합니다.
그리고 docker-compose.yml 파일의 command 부분을 다음처럼 수정합니다.
이렇게 하면, db 서비스의 5432 포트가 사용 가능할 때까지 기다린 후, Django 개발 서버가 실행되기 때문에 오류가 발생하지 않습니다.
3.6 도커 컴포즈의 주요 명령어
docker-compose 명령어를 짧은 alias로 등록해두면 편리합니다. 저는 oh-my-zsh에서 기본으로 제공하는 dco 를 사용하고 있습니다.
docker-compose.yml 파일의 내용에 따라 이미지를 빌드하고 서비스를 실행합니다. 자세한 진행 과정은 다음과 같습니다.
- 서비스를 띄울 네트워크 설정
- 필요한 볼륨 생성(혹은 이미 존재하는 볼륨과 연결)
- 필요한 이미지 풀(pull)
- 필요한 이미지 빌드(build)
- 서비스 의존성에 따라 서비스 실행
up 명령에 사용할 수 있는 몇 가지 옵션도 존재합니다.
- -d : 서비스 실행 후 콘솔로 빠져나옵니다. ( docker run 에서의 -d 와 같습니다.)
- --force-recreate : 컨테이너를 지우고 새로 만듭니다.
- --build : 서비스 시작 전 이미지를 4 우디의 선택을 이해하기까지 - CoAR(코아르) 새로 만듭니다.
현재 환경에서 실행 중인 각 서비스의 상태를 보여줍니다.
stop, start
서비스를 멈추거나, 멈춰 있는 서비스를 시작합니다.
서비스를 지웁니다. 컨테이너와 네트워크를 삭제하며, 옵션에 따라 볼륨도 지웁니다.
- --volume : 볼륨까지 삭제합니다.
실행 중인 컨테이너에서 명령어를 실행합니다. 자동화된 마이그레이션용 파일 생성이나 유닛 테스트, lint 등을 실행할 때 사용합니다. *
* 비슷한 명령으로 run 이 존재합니다. run 은 새 컨테이너를 만들어서 명령어를 실행합니다. docker run 과 마찬가지로 --rm 옵션을 추가하지 않으면, 컨테이너가 종료된 후에도 삭제되지 않습니다. (이런 이유 때문에 개인적으로는 exec 를 선호하지만, 컨테이너에서 추천하는 방식은 사실 run 입니다.)
서비스의 로그를 확인할 수 있습니다. logs 뒤에 서비스 이름을 적지 않으면 도커 컴포즈가 관리하는 모든 서비스의 로그를 함께 보여줍니다.
- -f : 지금까지 쌓인 로그를 다 보여준 후에도 셸로 빠져나오지 않고, 로그가 쌓일 때마다 계속해서 출력합니다.
3.7 도커 컴포즈 단축 명령어 등록하기
편의성을 위해 도커 컴포즈를 도입했는데 docker-compose 라는 명령어 자체가 너무 길어서 오히려 불편할 수도 있겠죠. 이를 위해 ~/.bashrc 나 ~/.zshrc 에 다음 내용을 추가합니다.
이제 docker-compose up 대신 dcup 을, docker-compose exec django bash 대신 dce django bash 를 실행하면 됩니다.
마지막으로, 몇 가지 삽질 경험을 바탕으로 나름의 팁을 정리해보겠습니다.
3.8 나름의 팁
3.8.1 docker-compose.yml 파일을 수정했다면?
docker-compose.yml 파일을 수정하고 이를 서비스에 적용하려면 서비스를 멈추고( stop ), 서비스를 지우고( rm ), 서비스를 시작해야( up ) 합니다.
하지만 up 명령만 실행해도, (현재 실행 중인 서비스 설정과 달라진 부분이 있다면) 알아서 컨테이너를 재생성하고 서비스를 재시작해줍니다. *
* 혹시 컨테이너를 재생성하지 않는 것 같다면, --force-recreate 옵션을 붙이면 됩니다.
3.8.2 Dockerfile-dev 파일을 수정했다면?
Dockerfile-dev 파일을 수정했을 땐 build 명령을 사용하여 도커 이미지를 새로 만들어야 합니다. 이후 서비스 중지와 삭제, 재시작을 해야 하죠.
하지만 up 명령에 다음과 같이 --build 옵션을 넣으면 알아서 이미지를 새로 만들고 서비스를 재시작합니다.
3.8.3 Dockerfile-dev도 신경 써야 함
개발용 이미지를 담당하는 Dockerfile-dev 파일과 배포용 이미지를 담당하는 Dockerfile 파일이 따로 존재한다는 점을 꼭 기억해야 합니다. 간혹 Dockerfile 만 고치면서 개발 환경에서 ‘외않돼?’라고 생각한 경우가 있습니다. (저만 그럴수도요;;)
3.8.4 데이터베이스 내용도 지우고 싶다면
열심히 개발하다 보면 데이터베이스에 원치 않는 데이터가 남는 경우가 있습니다. 데이터베이스에 접속해서 테이블을 삭제하거나 할 수도 있겠지만, down 명령에 --volume 옵션을 추가하면 서비스에서 사용하는 볼륨(=데이터베이스 데이터가 저장되는 곳)을 삭제하기 때문에, 데이터베이스를 깨끗하게 초기화할 수 있습니다.
3.8.5 MySQL보다는 PostgreSQL이 조금 더 편한 듯
로컬 컴퓨터에 데이터베이스 서버가 없어도, 서비스를 시작하기만 하면 알아서 데이터베이스 서버가 시작된다는 점은 굉장한 매력이었습니다. 그런데 MySQL과 PostgreSQL의 초기화 과정에 걸리는 시간이 꽤 차이가 나더군요.
제 컴퓨터 기준이긴 하지만 MySQL 초기화에는 대략 15초, PostgreSQL 초기화에 대략 5초 남짓한 시간이 걸립니다. 데이터베이스 초기화가 자주 필요한 일은 아니지만 그래도 15초와 5초의 차이는 꽤 크죠. *
* 아울러, 데이터를 로컬에 쌓기보다는 도커가 관리하는 가상 디스크를 사용하는 편이 속도 면에서 좀더 유리합니다.
3.8.6 ERROR: ‘No space left on device’
도커를 열심히 사용하고 있는데 갑자기 다음과 같은 메시지가 뜨면서 새 이미지를 빌드할 수 없는 경우가 발생합니다.
하드디스크에 용량이 엄청 많이 남아 있었는데도 말이죠. 이는 도커에서 사용하는 가상 파일 시스템의 기본 최대 값이 64기가바이트여서 발생하는 문제입니다.
주의! 다음 내용을 진행하면 도커 이미지와 컨테이너, 볼륨이 모두 사라집니다.
이 경우 도커의 환경 설정에서 Reset disk image 버튼을 누르면 해결됩니다.
도커 메뉴의 환경 설정을 선택합니다 Reset disk image 버튼을 누릅니다
이렇게 해서 개발 환경 구성하기를 마쳤습니다. 장황해보이지만, 실제로는 한 번 잘 구성해 두면 다른 프로젝트에는 그저 옮겨 붙이는 정도로 쉽게 적용할 수 있을 겁니다.
컨테이너란? 리눅스의 프로세스 격리 기능
🏷️ 키워드, 2020-01-23 - 리눅스 컨테이너는 운영체제 수준의 가상화 기술로 리눅스 커널을 공유하면서 프로세스를 격리된 환경에서 실행하는 기술을 의미합니다. 하드웨어를 가상화하는 가상 머신과 달리 커널을 공유하는 방식이기 때문에 실행 속도가 빠르고, 성능 상의 손실이 거의 없다는 장점이 있습니다.
핍 이해하기
사진 ⓒ IMDb
영화의 시작, 우디의 친구인 RC카가 빗물에 떠밀려가기 일보 직전이다. 아슬아슬하게 친구를 구하는 데 성공한 우디와 친구들. 엎친 데 덮친 격. 갑자기 등장한 한 남성이 앤디의 엄마와 몰리로부터 보핍과 양들(램프)을 건네받는다. 그리고 상자에 넣어 차에 실으려는 찰나. 그는 집에 키를 놓고 온 듯 다시 앤디의 집으로 돌아간다. 그 순간 보 핍과 양들을 구하기 위해 등장한 우디. 그러나 보는 이렇게 말한다. "이제는 다음 아이의 차례야. 매일 장난감을 잃어버리는 아이는 많아"라고. 그렇게 우디는 생각지도 못한, 차마 준비하지 못한 이별을 겪는다.
이 부분에 대해 허문영 영화평론가는 "가슴 아픈 상황에서도 그 순간 우디는 움직일 수 없고 자신에게 새겨진 하나의 표정밖에 지을 수 없다. 인형의 전적인 수동성이라는 운명의 쓰라림이 그의 부동과 무표정에 담겨 있음을 우리는 느낀다. (. ) 이 세계의 가혹한 율법이 요구하는 필연임을 메타적으로 드러날 때, 오히려 풍부한 감정이 표현된다는 점이다."라고 언급한다. [FILO NO.10, 에 대한 단상, P59] 어쩌면 시리즈의 팬이라면 분명 눈치챘을 것이다. 이상하다. 영화의 시작이 경쾌하지 않다. 여러 장르가 혼합된 앤디의 장난감들이 벌이는 일종의 소동극이 아닌, 9년 전에 벌어진 슬픈 이별을 보여준다.
사진 ⓒ IMDb
왜일까. 조금 더 영화 도입부에 대해 이야기를 해보자. 우디는 더는 '앤디'의 장난감이 아니다. 이제 그의 오른쪽 신발 밑창에는 'Bonnie'라는 이름이 적혀 있다. 위에서 보 핍이 언급한 '다음 아이'를 만난 것이다. 그러나 우디는 전혀 행복해 보이지 않는다. 심지어 보안관의 상징인 가슴에 달린 별 배지가 제시에게 달려 있다. 더 심각한 건 홀로 옷장에 남겨졌다는 사실이다. 그는 '보니의 장난감'이지만 그녀의 보안관은 아니다. 이 상황에 대해 친구인 버즈는 "괜찮아 친구? 다음엔 네가 선택 될거야"라고 걱정한다. 알고 보니 우디는 이번 주에만 세 번이나 보니의 선택을 받지 못했다.
는 이전 시리즈와 달리 조금 더 우디의 존재와 내면에 집중한다. 관객인 우리가 지켜봐야 하는 것은 무대 위에 오르는 우디의 모습이다. 영화 속 우디의 위치와 역할은 다른 장난감과 비교해 봤을 때 여러 측면으로 다가온다. 유치원에 가기 힘든 보니의 마음을 이해하는 부분에서는 '현자'나 '조력자'로, '포키'를 구하기 위해서 나서는 모습은 '영웅'으로 그리고 모든 장난감의 소중한 친구까지. 그러나 정작 장난감이라는 그의 존재는, 보니가 소유한 장난감이지만, 보니의 장난감이라 부르기 힘든 결핍감을 가진다. 결국 이 영화는 여러 서브플롯과 함께 '우디가 가지는 결핍'을 채워 넣기 위한 이야기이며, 이 글은 그 결핍을 이해하기 위한 탐구이다.
'토이 스토리 2', 사진 ⓒ IMDb
비루한 우디의 세속적 희망, 보 핌의 존재에 대한 물음
잠시 를 떠올려보자. 오른팔이 찢어진 우디는 고대하던 앤디와 카우보이 캠프에 함께 떠나지 못한다. 그렇게 고장이 난 채 방치된 그는 옛 친구인 팽귄 장난감 '위지'를 만난다. 위지는 고장이 나서 제대로 소리가 나지 않아 수리점에 맡길 계획이었으나 안타깝게도 앤디의 엄마는 앤디가 발견할 수 없는 천장에 숨겨놓았다. 그 순간 우디의 운명은 앤디의 장난감보다 위지와 함께 잊혀질 위기에 더 가깝다. 세계관에서 장난감은 결코 영원한 행복을 가질 수 없는 불안전한 존재이다. 물론 그들이 플라스틱에 불과한, 포키와 다를 바 없는 존재이기에 그들 역시 쓰레기라 불러도 무방한 부분도 내포되어 있다.
영화 속 그 어떤 누구보다도 보니의 애착인형인 '포키'를 지켜야 하는 이유를 잘 이해하고 있는 인물은 오직 '우디'뿐이다. 이것에 대한 근거로 위에서 언급한 '위지'의 존재와 자신이 망가져 본 경험으로 설명할 4 우디의 선택을 이해하기까지 - CoAR(코아르) 수 있지만, 결정적으로 우디는 '아이들이 왜 장난감을 필요로 하는지'에 대한 이해, '장난감의 존재와 역할'에 대해서 누구보다 가장 잘 깨닫고 있기 때문이다. 더 나아가 '앤디와의 경험'으로 하여금 아이들이 공통적으로 겪는 시기나 그들의 심리에 대해서 잘 관찰해왔기에 가능한 것이다. 다시 말하면, 우디를 움직이게 하는 것은 여전히 자신의 전 주인인 '앤디'와의 기억인 것이다. 그것이 자신의 존재를 설명하고, 타인('다음 아이'라 부른 편이 더 알맞은)을 이해한다.
사진 ⓒ IMDb
그러나 정작 '우디'는 이러한 자신의 이해에 대한 인식을 제대로 하지 못한다. 자신이 '포키'를 지키거나 '보니'를 위해서 애쓰는 부분에 대해서 그 어떠한 보상을 4 우디의 선택을 이해하기까지 - CoAR(코아르) 받을 수도, 심지어는 자신의 처지(자신이 더 사랑받는 장난감이 되는)가 나아지지 않는 '사실'을 망각한다. 아니. 알 수 없는 희망에 사로잡힌 듯 보인다. 이를테면 개비로부터 포키를 구출하던 도중 고양이의 습격으로 보 핍의 양이 떨어져 다리가 깨지고, 더키와 버니, 기글이 다치는 등 자신을 도와준 친구들이 부상을 당했음에도 포키의 구출에 집착하는 우디의 모습은 분명 보니를 위한 강박으로 다가온다.
한편으로, 우디가 문제를 해결하는 방법에 있어서 '박애적인 성격'을 띈다. 스피노자는 "박애란 우리가 불쌍하게 생각하는 사람에게 친절하려고 하는 욕망이다"라고 설명한다. [스피노자, ] 여기서 중요한 것은 우디가 불쌍하다고 생각하는 대상을 어떻게 이해하는지이다. 그는 포근함과 따뜻함과 같은 '함께 있으면 느끼는 느낌'으로, 즉 '앤디와 함께 했던 기억'(사랑에 가까운)으로 보니, 포키 그리고 개비까지 포용한다. 이러한 그의 방식의 '옳고, 그름'이나 보니의 행복과 무관하게 그의 행복은 여전히 해결할 방도가 없음을 우디는 알지 못한다. 아울러 주인이 어른이 된다는 자연의 법칙에 대해서 계속해서 망각하고자 부단히 노력하는 듯 느껴진다.
위험을 무릅쓰고 달리는 차 위에서 뛰거나, 자신의 심장 같은 소리박스를 건네주는 우디의 모습을 관객인 우리는 어떻게 볼 것인가.
위에 질문을 이렇게 바꿔 볼 수 있다. 우디가 가지는 희망은 대체 어떤 것인가. 이 희망은 그가 '일상에서 보는 희망'과 같은 것인가. 우디는 보니의 행복을 원하지만(우디의 희망), 정작 보니는 우디를 선택하지 않는다(일상의 희망). 독일 의학자 '헤르베르트 플뤼케'는 "일상의 희망은 세속적인 것, 필연적이지 않은 것, (. ) 언제나 대상과 관계된다. 그리고 피할 수 없이 허망한 성격을 지난다"라고 말한다. [헤르베르트 플뤼케, ] 인간과 장난감(주인과 노예)이라는 관계에서, 우디는 앤디와 보니까지 심지어 그의 장난감 친구들까지 행복할 수 있도록 나섰지만, 앤디를 지나 보니와의 관계 안에서 '이뤄질 수 없는 일상의 희망'(그들의 장난감으로, 영원히 함께 할 수 있는)을 가진다.
사진 ⓒ IMDb
그래서일까. '보의 존재'는 가볍게 여길 이벤트적인 요소나 페미니즘적인 관점이 아닌 분명한 이유로 영화 안에 건재한다. 에서 유독 인상적이었던 것은, 우디와 보 핍의 '숏-리버스 숏' 장면이다. 마치 사람 냄새가 난다고 표현할 만큼 두 장난감의 호흡은 분명 사람의 호흡과도 같은 에너지를 가진다. 정작 '보 핍의 존재'가 영화 속에서 빛나는 순간은 부러진 팔을 아무렇지 않게 테이프로 붙여 회전목마 위로 올라가 우디와 함께 카니발 전경을 바라보는 장면이다. 이후에 보는 우디에게 이런 말을 던진다. "내 인생을 선반에서 앉아 보내기 싫었어 (. ) 바깥세상을 여행해 볼 생각 해보지 않았어?" 그녀의 질문의 우디는 "주인이 없어?"라고 말하며 놀란다.
결과적으로 우디는 보니가 아닌 보 핍을 선택한다. 이 선택이 가진 이유는 결국 영화 처음으로 돌아갔을 때 비로소 이해할 수 있다. 자신이 보니의 선택을 가만히 앉아 기다리듯, 그렇게 보 핍이 떠나는 순간 어떤 선택도 할 수 없는, 자신의 부동과 무표정에 대한 환멸이 빗어낸 결심이다. '헤르베르트 플뤼케'는 그러한 우디의 선택에 대해서 "일상의 희망은 환멸을 안겨준다. 그런데 이 환멸에서, (. ) 신비롭게도 다른 희망이 생겨난다. 세속의 희망이 가져다주는 환멸은 분명 그 자체 안에 세속적인 것으로 이끌리는 환상으로부터 우리를 '해방시켜주는 가능성'을 품는다."라고.
우디는 그동안 함께 살아온 친구들과 이별을 한다. 물론 이 순간은 보니와의 이별이기도 하다. 더 나아가 지금까지 의 시리즈가 지속적으로 긴장을 주고 갈등을 형성시켰던 '사람-장난감'의 관계가 완전히 끝이 나는 순간이기도 하다. 여전히 카니발의 회전목마는 빙글빙글 돌고 있다. 자유를 선택한 우디는 자신의 내면의 주인이 되어 세상을 향해 나아갈 시간만이 존재한다. 이것의 그의 앞으로의 일상의 희망인 것이다.
사진 ⓒ IMDb
다시, 에서 우디의 무대는 어디였을까. 여전히 보니의 옷장? 기부 상자? 또 다른 아이의 방? 이제 우디는 그를 찾는 앤디의 목소리도, 어느 순간 다시 자신을 기억할 보니의 부름에도 흔들리거나 되돌아가지 않을, 자신의 내면의 목소리에 귀를 기울이는 존재이다. 그는 어디에 있어야 하는 존재가 아닌, 이제 어디든 다닐 수 있는 독립적인 존재로 다시 태어났다.
는 '우디의 해방'을 위해 짜여진 영화다. 어떤 순간에는 자신의 무력함을, 또 어떤 순간은 주인의 행복을 위한 노력을, 다른 어떤 순간은 친구를 지켜내기 위한 우정을 그리고 자신이 어떤 존재인지 깨닫는 순간까지. 어쩌면 우디는 보니의 행복이 아닌, 자신만이 보니를 행복하게 해 줄 수 있을 거라는 착각과 타자들에게 자신이 의미있고 가치있는 존재임을 보여 주고 인정받을 수 있는 자신의 모습에 빠져있던 것이 아닐까. 이 영화가 시리즈라는 프렌차이즈 영화로의 관점보다는 아직 끝나지 않은 가장 중요한 무언가가 남아 있음을 증명하고, 그것이 4 우디의 선택을 이해하기까지 - CoAR(코아르) 여전히 우리의 친구인 '우디'를 응원하고 스크린에서 마주할 수 있는 기회로 느낄 수 있는 작품으로 탄생한 것이 아닐까 생각을 해본다.
개발환경을 한 방에! 쉘 스크립트의 힘
안녕하세요. 우아한형제들 서비스개발실 주문시스템개발팀의 라태웅입니다.
어느 개발 조직이건 새로운 사람이 들어오면 그 사람의 PC에 새롭게 개발 환경을 셋팅해야 합니다.
이 때, 저는 세 가지 경우를 경험했는데요.
1번의 문제는 문서가 노후화되고 사람이 이해하기 쉽게, 따라하기 쉽게 작성하기가 어렵다는 단점이 있습니다.
2번의 문제는 전달자가 헷갈리거나 오래되어 잊어버린 경우 굉장한 혼선이 온다는 것, 거기에 신규 입사자 입장에선 이 조직이 과연 이대로 괜찮은가 하는 생각이 들 수도 있겠죠.
하지만 3번처럼 쉘 스크립트 한 4 우디의 선택을 이해하기까지 - CoAR(코아르) 방으로 셋팅이 된다면?!
신규 입사자의 감탄사가 들리시나요!? (와아아아아아아아아아. )
이 글에서 해볼 것
이 글에서는 두 가지 쉘 스크립트를 작성할건데요.
- PATH에 Working Directory 추가하기.
- AWS CLI, AWS ElasticBeanstalk CLI 설치하기.
- AWS Credential 추가하기.
보너스로 PHP Codeigniter를 AWS ElasticBeanstalk에 Deploy 해보기.
- 로컬 환경에서 Docker에 웹서버(PHP Codeigniter) 올려보기.
이 말인즉슨, 신규 입사자는 첫 번째 쉘 스크립트로 기본적인 AWS 기반의 개발 환경이 갖춰지고, 두 번째 쉘 스크립트로 로컬 환경에서 개발이 바로 가능해진다는 것이죠!
하지만 이 글에서는 쉘 스크립트의 문법에 대해서는 다루지 않습니다! 이곳을 참고해주세요! 이 글에서는 쉘 스크립트로 꽤 많은 것을 편하게 할 수 있다는 4 우디의 선택을 이해하기까지 - CoAR(코아르) 것을 알려드리는데에 초점이 맞춰져 있습니다.
첫 번째 쉘 스크립트
먼저 프로젝트 폴더를 생성하고, 내부에 디렉토리 구조를 만들어봅니다.
만들어진 폴더 구조는 아래와 같습니다.
이제 쉘 스크립트를 작성해봅니다.
이제 이 쉘 스크립트를 실행 가능한 상태로 만들어줍니다.
ls -al 명령어로 확인하면 실행 권한이 추가된 것을 확인할 수 있습니다.
이제 만든 쉘 스크립트를 실행해 볼까요?
AWS Access Key를 넣어주면..
이렇게 자동으로 Credential 까지 생기게 되죠!
AWS에 로그인 후 IAM 서비스로 이동합니다.
Groups > Create New Group을 눌러 새로운 그룹을 생성합니다.
Group Name을 적은 후 Next Step을 누릅니다.
테스트용으로 쓸거기 때문에 AdministratorAccess를 체크하고 Next Step을 누른 다음, Create Group을 눌러 그룹 생성을 마칩니다.
이제 유저를 생성할 차례입니다. Add User를 눌러주세요.
사진과 같이 적은 후 Next: Permissions를 누릅니다.
방금 만들었던 SSPJ 그룹에 유저를 추가하고 Next: Review를 누른 뒤, Create User를 눌러 유저 생성을 마칩니다.
SYSTEM : 독자이(가) Access Key을(를) 획득했다!
AWS ElasticBeanstalk에 CLI로 배포하기
먼저, webserver.zip 파일을 다운로드 받고, ~/sspj/webserver 폴더에 압축을 풉니다.
AWS ElasticBeanstalk에 관한 설명은 Elastic Beanstalk Configuration files(.ebextensions)에서도 보실 수 있습니다. 나는 쉘 스크립트만 궁금하다 하시는 분은 이 파트를 건너뛰셔도 됩니다!
PHP Codeigniter 기반으로 작성된 뼈대 웹서버 인데요. 이를 AWS ElasticBeanstalk CLI로 간편하게 배포해보겠습니다.
위의 eb 는 AWS ElasticBeanstalk CLI 인데요. 아까 첫 번째 쉘 스크립트가 자동으로 설치해준 녀석입니다. init 명령어를 통해 최초 설정을 해줍니다.
새로운 어플리케이션을 만듭니다.
어플리케이션 이름은 sspj로 해줍니다.
네. 우리의 웹서버는 PHP가 맞습니다.
저는 항상 최신 버전으로 개발하려고 노력합니다.
이 글에선 안 쓰지만 아쉬우니 SSH Key도 만들어 봅니다.
키페어 이름은 sspj-keypair로 하겠습니다.
패스워드는 자유롭게 설정합니다.
ElasticBeanstalk Application이 만들어졌습니다. 이제 Environment를 만듭니다.
Environment 이름과 도메인 Prefix를 설정합니다.
로드밸런서는 클래식으로 해봅니다.
로그가 찍히더니 성공했다고 나옵니다!
웹 콘솔로 접근해보니 잘 배포되었군요!
이제 다음 배포부터는 정말 간단합니다.
그런데 개발할 때 테스트를 위해서 기능을 수정할 때마다 deploy를 하고 수정하고 하는 식은 너무 불편하겠죠. 그래서 이를 Docker로 로컬 환경에서 실행해보겠습니다.
두 번째 쉘 스크립트
두 번째 쉘 스크립트에 작성하기 전에, webserver.zip 파일을 다운로드 받고, ~/sspj/webserver 폴더에 압축을 풉니다. 이미 앞선 파트에서 받으셨다면 받지않으셔도 됩니다.
저희는 Docker를 통해 개발 환경을 설정할 것이기 때문에 먼저 Docker를 설치해야 합니다. (이곳(Docker for Mac)에서 설치할 수 있습니다.)
먼저 Dockerfile을 만듭니다.
Dockerfile에 대한 자세한 설명은 진행하지 않습니다! 이런식으로 쉘 스크립트를 사용할 수 있다고만 봐주세요!
Docker에서 바라볼 config 파일을 생성합니다.
이제 이 Docker를 자동으로 빌드하고 실행하는 스크립트를 작성합니다.
쉘 스크립트 자체는 정말 짧지요?
실행해보겠습니다. (일단 실행 권한을 먼저 줘야겠죠?)
로컬에서 바로 접속이 됩니다!
이번 글에서는 쉘 스크립트를 한 번 만들어두면 개발 환경을 쉽게 구성할 수 있습니다! 라는 것을 전달드리고 싶었는데요… 다 적고보니 뭔가 뒤죽박죽이 된 것 같습니다 -0-…
동아사이언스
※편집자주: 3분만 투자하면 머릿속에서 최신 과학상식이 정리된다! 과학은 어렵고 딱딱하다는 선입견을 버리세요~ 출퇴근길, 등하굣길 등 언제 어디서나 쉽고 재미있게 볼 수 있는 ‘3분 과학’이 있으니까요! 그동안 우리가 잘 알지 못했던 궁금증이나 어려워서 이해하지 못했던 심층 과학까지 3분안에 큐레이팅 해드립니다.
옛날부터 사람들은 하얗고 깨끗한 피부를 선호해 왔습니다. 최근엔 건강미를 뽐내는 검은 피부도 인기지만, 흰 피부를 유지하기 위해 노력하는 사람들은 여전히 많습니다. 체모와 피부색을 결정하는 색소인 멜라닌은 외부 자극으로부터 피부를 보호하기 위해 만들어집니다. 멜라닌을 생성하고 얼굴을 칙칙하게 만드는 원인들을 모아봤습니다.
#1. 과도한 목욕과 때밀이
피부 면역을 지켜주는 층인 때를 벗겨내면 한 순간은 뽀얗게 보일 지언정, 오히려 외부 자극에 노출돼 더 많은 멜라닌이 생길 수 있습니다. 아예 없는 멜라닌은 적당히 있는 멜라닌만 못합니다.
#2. 호르몬 영향
멜라닌은 수많은 효소와 호르몬의 영향을 받아 만들어집니다. 특히 여성은 한 달 주기로 호르몬 변화에 맞춰 얼굴에 여드름이 생기거나 갑자기 낯빛이 어두워집니다. 물론 이 과정에서 멜라닌이 대량으로 만들어지진 않습니다. 그러나 테스토스테론이 여드름을 유발하듯 호르몬은 피부 상태를 크게 변화시킵니다.
#3. 잡티에 자주 손대는 습관
얼굴에 생긴 상처는 흉터가 생기면서 색소가 남아 주변보다 진하게 4 우디의 선택을 이해하기까지 - CoAR(코아르) 남는 경우가 많습니다. 색소 침착이라고 하는데, 상처로 인해 자극받은 것을 보호하기 위해 멜라닌이 생기기 때문입니다.
#4. 햇볕 아래서 돌아다니기
태양광선 중 파장이 200~400nm인 자외선에 자극을 받으면 피부를 보호하기 위해 멜라닌 색소가 만들어지는데요. 멜라닌을 합성하는데 큰 역할 을 하는 것은 비교적 파장이 긴 320~400nm 영역의 자외선A입니다. 자외선을 막기 위해서는 자외선 차단제를 애용하는 것이 좋습니다. 현대에는 각종 영양제나 음식물 덕분에 하루에 햇빛을 15분만 쬐도 비타민 D가 충분히 공급됩니다.
#5. 일상에서 스트레스 받기
스트레스가 직접 멜라닌을 만들지는 않지만 너무 심하면 세포 재생이 늦어져 노화가 일어나고 안색이 칙칙해 보일 수 있습니다. 밤늦게까지 잠들지 않아 잠이 부족해도 마찬가지죠. 그럴 땐 비타민C를 충분히 섭취하는 것이 좋습니다. 비타민C는 대표적인 항산화 물질로 스트레스로 인한 피부 노화를 막고 멜라닌 생성을 예방하는 효과도 볼 수 있습니다.
검어진 피부도 꾸준한 치료와 관리로 다시 하얀 피부로 돌아올 수 있습니다. 그러나 미리 자외선 차단을 꼼꼼히 해 흰 피부를 유지하는 것이 훨씬 쉬운 방법이겠죠?
0 개 댓글