도커 컴포즈(Docker Compse)로 워드프레스 블로그 만들기

오랜전부터 운영해오던 블로그가 한번씩 죽을때마다 백업을 신경쓰지 않아서 이미지들을 죄다 날리곤 했었다. 이 문제 때문에 검색을 좀 해봤더니 Docker 볼륨과 Git 을 통해 손쉽게 백업 문제를 해결하고 있었다. 왜 이 생각을 못했지? ㅎㅎㅎ 머리가 굳었어!

일단 옆지기 블로그(janeisyoung.com)를 깃헙 페이지에서 해방 시켜주기로 했다.

서버 구성

AWS EC2 인스턴스 2개를 이용해 하나는 Docker 호스트로 쓰고 있고 다른 하나는 데이터베이스 전용으로 쓰고 있다. 따라서 지금부터 쓰는 글은 데이터베이스가 원격지에 있다는 가정하에 쓰는 글이다.

데이터베이스 설정

원격 서버에 SSH 접근한 후 블로그용 데이터베이스를 생성하고 이 데이터 베이스만 접근 가능한 유저를 만들어준다.

// 루트 권한으로 MySQL 로그인
> mysql -u root -p

// DB 생성 
mysql> create database janeisyoung;

// 유저 생성
> create user janeisyoung@'172.31.%' identified by '<PASSWORD>';

// 권한 지정
> grant all privileges on janeisyoung.* to janeisyoung@'172.31.%' identified by '<PASSWORD>' with grant option;

// 권한 확인
> show grants for 'janeisyoung'@'172.31.%';

참고로 데이터베이스로 쓰고 있는 인스턴스는 AWS EC2 간에 172로 시작하는 내부 IP를 통해서만 접근하도록 제한된 상태다.

도커 컴포즈 설정

version: "3.3"
services:
  wordpress:
    image: wordpress:latest
    ports:
      - "8889:80"
    restart: always
    environment:
      WORDPRESS_DB_HOST: <DB_HOST_IP>:<DB_PORT>
      WORDPRESS_DB_USER: janeisyoung
      WORDPRESS_DB_PASSWORD: <PASSWORD>
      WORDPRESS_DB_NAME: janeisyoung
    volumes:
      - ./html:/var/www/html

도커 호스트 서버에 접속해서 docker-compose.yml 파일을 만들고 실행한다.

> mkdir janeisyoung
> cd janeisyoung
> docker-compose up -d

컨테이너가 제대로 만들어졌는지 확인해보자.

> docker ps -a
CONTAINER ID  IMAGE             COMMAND                 CREATED        STATUS        PORTS                 NAMES
5c204cc3f0a8  wordpress:latest  "docker-entrypoint.s…"  3 minutes ago  Up 3 minutes  0.0.0.0:8889->80/tcp  janeisyoung

Nginx 멀티 호스팅

도커에는 이미 여러 컨테이너 서비스가 올라가 있다. 마찬가지로 이번에 추가한 블로그도 그 중에 하나로 8889번 포트를 통해서 도커 호스트와 통신한다. 그리고 도커 호스트로 들어오는 모든 요청은 Nginx 를 통해서 각각의 컨테이너 서비스로 연결된다.

file: /etc/nginx/conf.d/janeisyoung.com.conf
---- 
server {
  listen 80;
  server_name janeisyoung.com;

  location / {
    proxy_pass http://127.0.0.1:8889;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Host $http_host;
  }
}

이제 Nginx 를 재시작한다.

> sudo systemctl restart nginx.service

도커 볼륨을 이용한 데이터 백업

앞에서 설정한 컴포즈를 실행하면 연결 해둔 볼륨(html 폴더)이 생기는데 이 폴더는 컨테이너 안쪽의 워드프레스 루트 폴더와 연동된다. 따라서 업로드한 모든 이미지와 설치한 테마, 플러그인 등등 모든 파일이 이 곳에 저장된다.

이제 도커 컨테이너가 어떤 이유로 죽더라도 내가 쓴글은 안전하게 DB에 저장되고 업로드한 모든 이미지는 볼륨 폴더에 저장되어있으므로 언제든 똑같이 되살릴수있다. 올레~ 그리고 이 볼륨 폴더를 git 연동해서 GitHub에 주기적으로 push 하면 서버를 이전할때도 손쉽게 이전할수있다.

Optimization React rendering

랜더링 최적화는 어떻게 하나?

1차적으로 불필요한 랜더링을 먼저 막습니다. 불필요한 랜더링이란 화면에 반영되는 데이터가 동일하거나 다른 화면에 가려져 랜더링이 아예 필요 없는 경우를 말합니다.

Props로 넘겨지는 데이터가 동일 할때 React.memo 를 사용하는 방법은 두 가지가 있습니다.

1. 기본 탑재된 비교 알고리즘을 쓰는 경우

React.memo(컴포넌트)

기본 알고리즘을 쓰는 경우는 Props로 넘어오는 값들이 이뮤터블임을 전제로 합니다. 뮤터뷸 객체가 넘어오면 당연히 이 전략은 통하지 않습니다. 여기서 신경써야하는 것들이 바로 핸들러나 콜백 함수들인데, JS에서 함수는 뮤터뷸한 객체라서 useCallback 으로 일일이 감싸서 캐시(메모이제이)하지 않으면 효과가 없습니다.

그렇다고 항상 useCallback 쓸 필요도 없습니다. 캐시 비용도 비용이지만 매번 콜백 함수를 감싸는 귀차니즘도 만만치 않기 때문에 그 비용이 오히려 비효율일 경우가 많습니다. 이럴때는 비교 로직을 커스텀 하는게 더 효과적 입니다.

2. 비교 알고리즘을 커스텀 하는 경우

React.memo(컴포넌트, 비교로직함수)

비교로직을 커스텀 할경우엔 콜백함수를 useCallback 으로 넘기든 넘기지 않든 랜더링에 필요한 요소들만 선택적으로 찾아서 랜더링을 결정합니다. 따라서 메모된 값을 쓰고 싶을 경우엔 true, 새로 랜더링을 할 경우엔 false를 비교 함수에서 반환합니다.

최적화 우선순위

모든 컴포넌트를 다 useCallback 으로 감싸거나 React.memo로 감쌀 필요는 없습니다. 병목 지점을 먼저 찾고 필요한 곳 위주로 처리하면 됩니다.

예를들어 아래와 같은 구조에서 최상위 Root 컴포넌트가 랜더링되면 나머지 A~F 컴포넌트가 줄줄이 랜더링 됩니다. 이때 A가 한번 랜더링 되면 F 같은 리스트 아이템들은 수없이 많이 랜더링 됩니다.

  ---Root
      |--- A
           |-- B ( List )
               |-- F
               |-- ... N 개의 F 
               |-- F
           |-- C
      |-- D

즉, 위와 같은 구조에서 F 컴포넌트가 제일 먼저 최적화 대상이 됩니다.

극단적인 랜더링 최적화 (한번만 그리기)

가끔은 컴포넌트를 딱 한번만 그리는 경우도 있습니다. 예를 들면 고정형 네비게이션바 같은 경우엔 Props를 주입받지만 화면을 그리는 요소에 영향을 주지 않기 때문에 이런경우엔 극단적인 선택을 할수있습니다.

export default memo(NavigationBar, () => true);

클로저 변수와 함께 콜백 함수 메모하기

   1번
   const 핸들러함수 = useCallback(()=>{
         const currentItem = items[currentIndex];
      //  currentItem 처리
   },[currentIndex])

    2번 
    const 핸들러함수 = (currentItem) => {
         // currentItem 처리....
    }   

    <List>
        {for item in items 
            return <ListItem item={item} onPress={핸들러함수}>
         }
     <List>

1번처럼 useCallback 함수를 사용해 클로저 변수를 함께 패키징 할 경우 핸들러 함수는 currentIndex 값에 따라 매번 다른 함수가 만들어집니다.

그리고 이것을 <ListItem> 컴포넌트의 프로퍼티로 넘기기 때문에 <ListItem> 컴포넌트의 메모 전략은 매우 단순하게 가져갈 수 있습니다.

export default memo(ListItem);

물론 currentIndex 갯수 만큼 메모하는건 그만큼 약점입니다.

반대로 useCallback 을 쓰지 않을 경우에는 당연히 ListItem의 최적화 전략이 달라져야합니다.

export default memo(ListItem, (prev: Props, next: Props) => {
  return prev.language === next.language;
});

그리고 2번처럼 핸들러 함수에 넘겨받은 값을 그대로 같이 반환해야합니다.

function ListItem(props){
  const { item } = props;

  return( 
     <TouchableOpacity onPress={() => onPress(item)}>
       // 블라블라...
     </TouchableOpacity>
  )
}

주의! 최적화 전략은 케바케라서 상황에 따라 적절히 취사 선택하려면 잘 알아야한다. 암튼 간만에 회사 일 하다가 긴 PR을 올려서 그대로 블로그에 옮겨 왔음.

2019년 회고

회고 시즌이다. 최근 전전 회사 송년회를 갔다가 현재의 나를 되돌아보는 개기가 됐다. 도무지 나는 개발 외에 다른 대화 주제에 끼가 어려웠다. 나와 같은 공간에서 일 하는 사람들이 절반인 이 그룹에서 도대체 이들은 어떻게 이런 관심사를 유지하고 있는거지? 되돌아가는 길에 생각이 많아졌다. 바쁘다는 핑계로 나를 놓아 버린건 아닌지 덜컥 겁이났다.

어제도 옆지기와 한해를 마무리하며 물었다. 나는 변한거 같니? “응! 좀 더 나은 방향으로 변했어!” 그랬다. 나는 변했고 성장했다. 돌이켜보면 “바빴음”은 핑계가 아니라 나의 선택이었다.

올 한해는 일에 파묻혀 살았다. 그간 자잤던 이직탓에 이번엔 오래 다닐수 있을까 싶었는데 순식간에 1년 3개월이 지났다. 그동안 심어 놓은 잔디가 나를 증명한다. 10월에는 그동안 달려온 나를 위해 긴 휴식을 줬다. 휴가 기간엔 사실 회사일만 놓았지 코드는 여전히 놓지 못했다.

스쿼시 머지 정책에 내 커밋들이 하나로 퉁쳐진게 이 정도지 사실 코드를 놓은 날이 거의 없었다. 주말에도 간간히 로그가 찍힌 걸 보니 참 열심히도 살았다.

회사일
회사일 + 개인 프로젝트

작년 6월 부터는 사실상 매니저 역할에도 손을 뗐다. 조직을 좀 더 나은 방향으로 움직이기 위해서 권한이 필요했고 매니저의 역할이 중요하다고 생각 했는데 막상 디렉터라는 타이틀을 달고보니 내가 원하는 권한은 없고 그냥 일만 더 많아졌다.

그래도 자초한 일이라 잘해보려고 이런저런 시도를 했다. 그 중에서 개인 인터뷰를 했던 두달의 시간이 그나마 즐거웠다. 바쁘다보니 9명을 인터뷰해서 전체 공개하는데 두달이 걸렸다. 참고로 인터뷰 내용은 PR 과정을 통해 보정되고 전체 공개된다. 물론 사전에 공개를 꺼리는 답변은 둘만의 비밀로 간직하고 있는데 사실 기록하지 않은 내용이라 금새 다 잊어먹었다. ㅎㅎㅎ

앱 개발

내가 매니징을 포기하게 된 원인은 바로 이 앱개발 때문이었다. 그동한 혼자서 고군 분투 하던 멤버가 안쓰러워 백업 하러 갔다가 박제가 되버렸다. 당시 내가 앱 개발을 백업 해야겠다는 생각은 의심할 여지없이 너무 자연스러운 결정이었다. 만약 내가 매니징만 하는 매니저 였다면 아마 다른 선택을 했었을것 같다. (못해! 불가능해!)

어쨌든 이 결정으로 인해 나는 10년이 넘는 내 커리어에서 두번째 번아웃을 겪었다. 건강에 적신호가 켜지고 알러지를 얻게 됐는데 다행히 지금은 나아지고 있다. 이 일로 지난 시절 열심히 운동했던 나에게 큰 감사를 하게 됐다.

반면 얻은 것도 많다. 먼저 사람을 얻게됐고 기술에 대한 자신감도 얻었다. Swift 는 원래 좋아했던 언어였고, Kotlin도 새롭게 알게됐다. Java는 좋아하는 언어는 아니지만 RN 모듈을 보다보면 어쩔수없이 Java와 Object-C도 알게되더라… ㅎㅎㅎ 여기에 TypeScript를 주언어로 쓰게되면서 그동안 type에 대한 거부감도 사라졌다.

자세한 앱개발 후기는 조금씩 정리해서 올해는 글로 써야겠다. 어쨋든 앱을 다시 만든다면 주저없이 나는 RN을 선택하게 될것 같다. 실제로 지금 개인 프로젝트로도 RN을 쓰고 있다.

34일의 휴가와 여행

휴가는 작년에 총 34일을 썼는데 숫자만 놓고 보면 많이 썼다. 회사가 무제한 휴가인건 분명 좋은 점 같다. 휴가를 면밀히 들여다보면 연초에 잡힌 강의로 10일과 번아웃 회복에 쓴 13일을 빼면 개인 휴가는 11일 밖에 안된다. 숫자만 많아보이지 왠지 진 기분이든다.

1월 충주 수안보

너무 추워서 짧은 일정에 온천을 다녀왔다. 이때만해도 차가 없어서 대중교통으로 다녀왔는데 날씨가 추워서 인지 시골이 원래 그런거인지 참 맴이 좀 그런 여행이었다.

5월 발리 2주

아내의 강력한 의지로 발리행 비행기에 몸을 싣고 2주간 원격근무를 하게 됐다. 원격근무가 일상인 나에게 발리라고 크게 다를쏘냐 싶었는데 확실히 발리에서 생활은 한국보다 쾌적하다. 인터넷이 간간히 끈겨서 불안하긴한데 무엇보다 규칙적인 생활을 하게 되는게 가장 크다.

9월 가족여행

부모님과 동생네까지 동반 세 가족이 매년 300만원이 모이면 떠나자고 부은 여행계가 첫 결실을 맺고 어렵게 일정을 잡아서 떠났다. 때마침 엄청난 태풍의 여파로 한가했다. 하지만 역시나 여행은 날씨가 절반! 다음엔 좀더 좋은 날에 갔으면 좋겠다.

여름 손님과 사랑방 손님

재작년 이사하는 날에 캐나다 친구가 우리집에 놀러온걸 시작으로 우리집은 게스트하우스처럼 많은 손님들이 왔다 갔다. 지인의 지인 친구인 호르헤(칠레계 미국인)를 시작으로 발리에서 알게된 폴란드 친구 막다와 인도 친구 프리야. 사실 나보다는 아내 지인이라고 봐야지. 암튼 그리고 호랑이보다 무섭다는 여름 손님 민재가 두달간 우리집에서 머물게 됐다! 뚜뚱~!

신기한건 이들 모두 발리에서 만난 친구들이고 이들 모두 다녀가고 난 뒤에야 그토록 기다리던 우리 아들 베베가 찾아왔다.

PC 게임 VS 보드 게임

라이엇게임즈에 잠깐 몸 담았던 계기로 리그 오브 레전드를 심심치않게 하게됐는데 민재가 우리집에 온 날을 시작으로 다시 롤을 시작했다. 역시 게임은 친구가 있어야 제맛! 최근 회사 스트레스를 빌미로 게임에 한창 빠져있었다. 이리 살면 안되겠다 싶어서 올해만 삭제를 세번째!!

이번에는 아내에게 절대 다시 게임을 설치하지 않겠다고 다짐하고 게임하고 싶을땐 게임방 가겠다고 선언했다. 아무튼 그 이후로 나는 아직까지 롤을 하지 않고 있다. 가끔 하고 싶다는 생각이 드는데 그때마다 예전처럼 다시 코딩을 한다.

보드 게임도 어쩌면 민재덕이다. 발리에서 같이 재밌게 한 경험이 쌓이다보니 나도 아이 생기면 보드게임을 해야겠다 싶어서 마트를 갈때마다 보드게임 코너를 유심히 본다. 그렇게 사온 보드 게임이 벌써 5개쯤 되는거 같다. 언제 베베랑 같이 하려나… 🙂

원격 근무와 알러지

올해 나를 괴롭히는 최대 적으로 떠올랐다. 바빠서 자연스레 재택근무가 많아지다보니 건강에 적신호가 켜졌다. 눈뜨면 씻지도 않고 의자에 앉아서 아침 8시부터 그대로 저녁 9시 까지 앉아 있는날이 수도 없이 많다보니 다리가 퇴화될 지경에 이르렀다.

가끔 회의 때문에 어쩔수없이 출근하는 날엔 다리가 엄청 쑤시고 아팠다. 그러다가 지난 8월부터 알러지가 올라오기 시작하더니 최근까지 한참 고생했다. 건강 검진에는 무려 160여가지 알러지 테스트에 절반 이상에 알러지 반응이 나타났다.

예전에도 가끔 와인 마시다가 팔에 잠깐 올라왔던 적은 있었는데 이번처럼 온몸에 급속도로 퍼지는 속도가 약을 먹지 않고는 견디기 힘든 지경에 이르러 사태에 심각성을 알게됐다. 건강엔 나름 자신있던더라 진짜 자만했다. 역시 나이는 못 속인다. 최근에 페이스북에 알러지에대한 심경을 토로 했더니 페친들이 염려와 격려에 눈물이… ㅜㅜ

그 이후에 의식적으로 일을 줄이고 있다. 일에 과 몰입하는 습관을 없애기위해 일부러 출근도 하고 집보다는 가까운 카페에서 일 하려고 노력하고 있다. 다행이 이런 노력이 헛되지 않았고 최근 한달간 약없이 잘 지내고 있다. 알러지가 올라와도 전신으로 퍼지지 않고 이내 자자들고 있다. 리모트 하려고 이곳으로 왔는데 다시 출근하는 회사로 이직할까 싶다.

개인 프로젝트와 신년계획

오프라인 강의

강의는 늘 부담이지만 부담감을 통해서 내가 알고 있는 지식들을 정리하게 되고 수강생들의 긍정 피드백을 받으면 보람도 느낀다. 매번 강의 할때마다 이번이 마지막이야 했지만 의뢰가 오면 또 고민하다가 하게되겠지. 물론 거절한 강의도 무려 3건이나 있다. ㅎㅎㅎ

올해는 Javascript, React 와 더불어 React Native, NodeJS 강의 가능합니다. 메일(miconblog@gmail)로 문의주세요.

개발자 방송: TV 플루토

작년초까지 나름 취미 생활로 잘 이어가다가 바빠서 흐름을 한번 놓치니까 더이상 방송할수 없는 주제가 되어버렸다. 한창 만들던 코딩 프로젝트가 Nextjs5 버전이었는데 벌써 9 버전이라니 ㅎㄷㄷ 몇주전부터 Nextjs로 다시 개인 프로젝트를 시작했는데 방송 분량이 확보되면 다시 시작해야겠다.

가족계획

정말 거짓말처럼 마음을 비우니까 찾아왔다! 지금은 엄마 뱃속에서 잘 크고 있다. 올해가 더욱 기대된다. 지인들이 육아휴직 쓰라던데 우리회사가 육아휴직 제도가 있나 모르겠다. ㅎㅎㅎ

겁쟁이 사자

아내와 마트에 가면 내가 꼭 들르는 코스가 있는데 맥주 코너와 의류코너 그리고 문구점 코너다. 그러다가 최근 의류 코너에서 내 눈을 사로 잡는 옷을 발견했다. 개인적으로 사자를 좋아해서 영어 이름도 레오를 쓴다. 참고로 스페인어로 레오는 사자다. 아무튼 마트에서 발견한 사자 그림이 너무 귀여워서 아내를 꼬셔왔더니 “이거 오즈의 마법사 시리즌데?”

그래서 깡통나무꾼과 겁쟁이 사자를 집으로 들였다. ㅋㅋㅋ 그리고 깃헙 ORG에 겁쟁이 사자를 선점했다. 앞으로 개인 프로젝트는 “겁쟁이 사자”라는 이름으로 출시 해야지!

아내는 “멋쟁이 사자처럼” 아류라고 하지만 절대 아니다. 이건 달라! 무려 겁쟁이 사자라고! 나도 용감해지는 알약먹으면 용감해진다고!!