[TIL] 스팸메일 발송에 의한 사이트 다운 현상

몇일전부터 블로그가 자꾸 죽는 문제가 발생해서 프로세스 목록을 살펴보니 sendmail 프로세스 수십개가 떠 있었다.

혹시나 싶어서 로그를 열어보니…

tail -f /var/log/maillog

뭐지? 이쎄한 느낌은,… 172…로 보내는건 내가 보내고 있는거잖아! -_-;…

Oct 18 03:49:50 ip-172-31-5-227 sendmail[4724]: v9I3nobO004724: to=brownjerry359@yahoo.com, ctladdr=armaan.h@miconblog.com (498/498), delay=00:00:00, xdelay=00:00:00, mailer=relay, pri=32155, relay=[127.0.0.1] [127.0.0.1], dsn=4.0.0, stat=Deferred: Connection refused by [127.0.0.1]
Oct 18 03:49:50 ip-172-31-5-227 sendmail[4725]: v9I3noaq004725: Authentication-Warning: ip-172-31-5-227.ap-northeast-1.compute.internal: nginx set sender to armaan.h@miconblog.com using -f
Oct 18 03:49:50 ip-172-31-5-227 sendmail[4725]: v9I3noaq004725: from=armaan.h@miconblog.com, size=2125, class=0, nrcpts=1, msgid=<d8462c873023f84d2f56c38bc80cd9b2@miconblog.com>, relay=nginx@localhost
Oct 18 03:49:50 ip-172-31-5-227 sendmail[4725]: v9I3noaq004725: to=rdsjets@gmail.com, ctladdr=armaan.h@miconblog.com (498/498), delay=00:00:00, xdelay=00:00:00, mailer=relay, pri=32125, relay=[127.0.0.1] [127.0.0.1], dsn=4.0.0, stat=Deferred: Connection refused by [127.0.0.1]
Oct 18 03:49:50 ip-172-31-5-227 sendmail[4726]: v9I3nosf004726: Authentication-Warning: ip-172-31-5-227.ap-northeast-1.compute.internal: nginx set sender to armaan.h@miconblog.com using -f
Oct 18 03:49:50 ip-172-31-5-227 sendmail[4726]: v9I3nosf004726: from=armaan.h@miconblog.com, size=2184, class=0, nrcpts=1, msgid=<51ad53d80a417ec81c68421db28d9432@miconblog.com>, relay=nginx@localhost
Oct 18 03:49:50 ip-172-31-5-227 sendmail[4726]: v9I3nosf004726: to=livingvampire@hotmail.com, ctladdr=armaan.h@miconblog.com (498/498), delay=00:00:00, xdelay=00:00:00, mailer=relay, pri=32184, relay=[127.0.0.1] [127.0.0.1], dsn=4.0.0, stat=Deferred: Connection refused by [127.0.0.1]
Oct 18 03:49:50 ip-172-31-5-227 sendmail[4727]: v9I3noQL004727: Authentication-Warning: ip-172-31-5-227.ap-northeast-1.compute.internal: nginx set sender to armaan.h@miconblog.com using -f
Oct 18 03:49:50 ip-172-31-5-227 sendmail[4727]: v9I3noQL004727: from=armaan.h@miconblog.com, size=2127, class=0, nrcpts=1, msgid=<dd97de13b625fd97de59cf2a002743c9@miconblog.com>, relay=nginx@localhost
Oct 18 03:49:50 ip-172-31-5-227 sendmail[4727]: v9I3noQL004727: to=hanklan@hotmail.com, ctladdr=armaan.h@miconblog.com (498/498), delay=00:00:00, xdelay=00:00:00, mailer=relay, pri=32127, relay=[127.0.0.1] [127.0.0.1], dsn=4.0.0, stat=Deferred: Connection refused by [127.0.0.1]

해킹 당한건가? 그럴리가 없는데,.. 80포트 밖에 열어둔게 없는데 이런게 가능한걸까?

다행인건 sendmail 설정이 유효하지 않아서 발송된 메일이 전부 거절 당했다. 덕분에 보내진 메일은 응답을 받기위해 프로세스를 들고 있다가 타임아웃이 하나둘 걸리면서 행이 걸린거 같다. 줵일~

샌드메일 발송부터 해결하자.

일단 급한대로 sendmail 데몬부터 내렸다.

$> service sendmail stop 

그리고 매번 리부팅할때마다 샌드메일 데몬이 뜨길래 아예 지워버렸다.

$> yum remove sendmail

원인이 뭐였을까?

기억을 더듬어 봤다. 아마도 예전에 워드프레스에서 댓글이 달리거나 이럴때 메일을 받았으면 해서 직접 sendmail을 설정해볼까 싶어서 설치했던 기억이 났다.
그리고 아니나다를까 워드프레스 플러그인 항목에 버젓이 Check email 플러그인이 똭~!!
아마도 이 녀석이 범인인것 같다. access 로그에도 남았겠다싶어서 확인해봤다.

[18/Oct/2017:03:52:30 +0000] 200 211.243.39.14 http://miconblog.com/wp-admin/tools.php GET /wp-admin/tools.php?page=checkemail HTTP/1.1 9689 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
[18/Oct/2017:03:52:31 +0000] 200 211.243.39.14 http://miconblog.com/wp-admin/tools.php?page=checkemail GET /wp-json/jetpack/v4/jitm?message_path=wp%3Atools_page_checkemail%3Aadmin_notices&query=page%253Dcheckemail&_wpnonce=4bad6409be HTTP/1.1 12 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
[18/Oct/2017:03:53:31 +0000] 200 211.243.39.14 http://miconblog.com/wp-admin/tools.php?page=checkemail POST /wp-admin/admin-ajax.php HTTP/1.1 58 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
[18/Oct/2017:03:53:32 +0000] 200 211.243.39.14 http://miconblog.com/wp-admin/tools.php?page=checkemail GET /wp-admin/options-general.php HTTP/1.1 21614 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36

역시나 뭔가 흔적이 남아있다. checkemail 플러그인을 바로 삭제하고 범인 수색을 더 해봐야겠다. 블로그를 독립시키던가 해야지 이 녀석 때문에 엄한 내 다른 서비스들도 같이 피해를 봤다 -_-;..
DB서버를 독립시키든지 블로그를 독립시키든지 빠른시일에 해야겠다.. 아우~ 귀찮아.

React에 Flow 적용기

타입은 왜 필요한가?

개인적인 경험에서 타입에 대한 필요성을 느끼기 시작한 것은 이뮤터블JS를 쓰기 시작하면서부터다. 이 녀석은 실제로 까보지 않으면 안에 뭐가 들어가 있는지 참 알기 어렵다. 최근 코드 리뷰에서도 “이게 뭐하는 녀석이죠?” 라는 질문을 자주 하는 나를 발견한다. 가령 어떤 함수에 이뮤터블 객체 A 를 인자로 넘겨버리면 저 A 안에 어떤 값이 있는지 알기 어렵다. 그래서 코드를 따라가기를 반복하다 지치면 결국 작성자에게 물어보게 된다. 이럴때마다 타입이 있었으면 하는 생각도 들고 차라리 주석을 꼼꼼히 달아보자고 제안을 해볼까? 하는 생각을 해보기도 하지만 역시.. 후자는 나도 잘 안하기 때문에 결국엔 약간의 강제가 필요하면 좋겠다는 생각을 했다.

Flow와 TypeScript

최근엔 타입스크립트에 대한 주목도와 관심도가 높아지는(?).. 관심도가 높은지는 사실 모르겠고, 그냥 그런 분위기가 있어서 나도 해야하는게 아닐까싶어서 TS 주변을 기웃거렸다. 컨퍼런스도 가고 주변에 TS 좀 한다는 녀석들에게 레퍼랜스를 물어물어서 시작하긴 했는데….

아!!!!!!!!!!!! 빡쳐!!!!
이게 결론이다. TS를 하려면 처음부터 해야지 중간에 잘돌아가는 환경을 뒤엎을려니 쉽지 않다. 결국 기존 react 기반 프로젝트에 ts를 적용하는 건 실패~~

이렇게 또 몇주가 지나고 TS와 씨름에 지쳐갈때쯤 내가 왜 이러고 있나 싶기도하고 그러다가 미워도 다시한번 Flow를 어제부터 다시보기 시작했다. 그런데 너무 간단하다. 헐퀴!! 나 그동안 뭐한거니…-_-;;;

Flow 설치

create-react-app 으로 시작한다면 Flow 환경의 절반은 이미 설정되어 있다. 밥상에 숟가락 얻는 정도다.

1. 먼저 flow-bin 모듈을 설치한다.

$> npm install --save-dev flow-bin  // or yarn add --dev flow-bin`

2. package.json 파일을 열어서 “scripts” 섹션에 "flow": "flow"를 추가한다

  "scripts": {
    "start": "node scripts/start.js",
    "build": "node scripts/build.js",
    "test": "node scripts/test.js --env=jsdom",
    "flow": "flow"       <======= 요기!
  }

3. Flow 환경 설정 파일을 만든다.

아래 명령을 실행하면 루트 폴더에 .flowconfig 파일이 생성된다.

$> npm run flow init
$> npm run flow

.flowconfig 기본 설정

[ignore]  
.*/node_modules

[include]  
./src

[libs]

[options]

4. 외부 라이브러리에 대한 타입 정의를 추가한다.

아래 명령을 실행하면 루트폴더 하위에 /flow-typed 폴더가 생성되면서 외부 라이브러리에 대한 flowType을 찾아준다. 물론 모든 라이브러리를 찾아주는건 아니다.

$> npm install -g flow-typed 
$> cd /path/to/my/project 
$> flow-typed install

5. 타입 체크를 원하는 파일에 // @flow 라는 주석 달기.

Flow 컴파일러는 3번에서 설정한 환경에 따라 파일을 읽게 되는데, 파일 상단 주석에 // @flow 라는 어노테이션을 지정한 파일만 컴파일한다.

6. 인텔리J(WebStorm)에서 Flow Type Checker 적용하기

  1. 일단 인텔리J에 NodeJS 플러그인을 설치하고 Enable 시켜놓자.
  2. Flow Type Checker 모듈을 설치한다. 전역으로 하든 로컬로 하든 알아서 판단하자.

    $> npm install -g flow-bin // global
    $> npm install flow-bin // local

  3. 인텔리J에 Flow를 사용하겠다고 설정한다.

    Cmd+(,) > Languages & Frameworks > JavaScript > choose Flow

  4. 3번에서 Flow executable 필드에 2번에서 설치한 flow-bin 을 지정한다

Flow 적용

Flow를 이용해 타입 체크를 하는 이유를 잘 생각해봐야한다. 진행중인 프로젝트에 어떤 부분을 타입으로 만들어서 점검할지 생각하면서 적용하자. React-Redux 프로젝트에 Flow를 적용한다면 이 문서를 참고하자.

create-react-app with Flow

내가 React로 프로젝트를 시작하는 가장 큰 이유는 아키텍처를 내가 선택할수있기 때문인데, 아키텍처에 집중하기 위해서는 사실 기본기가 튼튼해야한다. 큰 그림을 그리는데 JSX 문법을 몰라 삽질한다거나 한땀한땀 웹팩 구성하느라 시간을 허비하기엔 그 시간이 좀 아깝다. 그런면에서 create-react-app 으로 리액트를 시작할 것을 강력히 추천한다. 이와 같은 이유로 최근엔 TS를 적용한 create-react-app-typescript도 생겼다.

트러블슈팅

Q1. 특정 라인에 대한 에러 체크를 하고 싶지 않아요!

$FlowFixMe 를 이용하세요.

// $FlowFixMe 
import './UserProfile.less';

Q2. flow-typed 를 이용해 외부 라이브러리에 대한 타입을 설치하긴 했는데, immutablejs는 없네요 ㅜㅜ

immutable.js 는 라이브러리 안에 타입을 내장해서 배포하고 있습니다. 일단 .flowconfig 파일을 열어서 [include] 항목에 아래와 같이 추가합니다.

[include]
./src
.*/node_modules/immutable/.*

그런데 이것만 가지고는 충분하지 않습니다. 그래서 누군가 이걸 해결한 타입정의 파일이 있어요. 일단 요 파일을 /flow-typed/ 폴더하위에 immutable_vx.x.x.js 라고 저장해주세요.

참고문서

  1. create-react-app#adding-flow
  2. https://flow.org/en/docs/
  3. https://raw.githubusercontent.com/facebook/immutable-js/master/type-definitions/immutable.js.flow
  4. https://www.jetbrains.com/help/webstorm/2017.1/flow-type-checker.html

React를 SpringBoot에서 서버사이드 랜더링하기 #1

최근 React Korea 커뮤니티에서 Spring과 React를 접목하려는 분들이 많아서 개인적으로 왜 저런 조합을 가져갈까 의아해 했는데 그 이유를 이제야 알았다. 첫번째 이유는 우리나라에 Java 개발자가 많다는 것이고, 두번째는 그 개발자들이 주로 서버 프로젝트로 Spring을 쓰기 때문이고, 그래서 많은 회사들은 Spring을 레거시로 들고 있을테고, 개발자는 여전히 힘들고, React는 뭔가 쉬울것 같고, 그러다보니 기존 레거시에 한번 적용해보고 싶은 욕망은 당여한거고, 막상해보니 어렵고,… 뭐 이런 흐름과 생각이 존재한다는 것을 최근에 복직하고서 알게 됐다. 아무튼 그래서 이번 포스트는 그런 개발자들을 수고를 좀 줄여보고자 정리해봤다.

참고로 나는 Java를 jdk 1.6이 막 나오던 시절에 쓴게 마지막이고 Spring 2.0이 막 나오던 시절에 설정에 치여서 접었던 경험이 있다. 한마디로 Java와 Spring은 내 전문 분야가 아니다. 따라서 뭔가 친절하고 디테일한 설명이 없음을 미리 말해둔다.

0. 삽질을 막기 위한 안내

최신 트렌드에 맞게 일단 모든걸 최신버전으로 해봤다. 따라서 아래 조건이 아니면 안될수도 있음을 미리 말해둔다. React가 IE8 이하 버전은 사실상 버렸기 때문에 웹프로젝트로 React를 쓰려는 사람은 일단 말리고 싶다. 굳이 또 그 어려운 길을 걷겠다면 말리진않겠다. 참고로 IE8을 쓰려면 React 1.4 버전을 써야하고 ES3를 위해 몇가지 pollyfil을 해야하니 알아서들 잘 찾아보시라. IE8도 지원안한다면서 이게 쓸만한건가? 라는 생각이 들수도 있을텐데 다행히 모바일웹에는 IE가 없기 때문에 모바일웹만 서비스한다면 괜찮은 조합이 아니라 아주 훌륭한 조합니다.

  • SpringBoot + JSP + Gradle
  • JDK 1.8 (Java8 nashorn Javascript Engine)
  • React 1.5
  • Webpack

1. SpringBoot 시작하기

SpringBoot는 스프링 이니셜라이져라는 사이트에서 설정을 스켓폴딩했다. 이 사이트에서 디펜던시로 ‘Web’을 추가하고 다운로드받자. 그리고 압축을 풀어서 인텔리J로 연다. 그럼 인텔리J가 빌드를 뭘로 할꺼냐고 묻는다. 나는 Gradle로 선택했다.

2. SpringBoot 서버 설정하기

다음으로 인텔리J에서 서버를 추가하자.

서버는 SpringBoot를 선택한다.

Name과 Main class 그리고 Use classpath of modthod를 지정한다. 마지막으로 JDK는 1.8로 지정한다. Java8에는 크롬V8 자바스크립트 엔진과 호환되는 nashorn(나스호른, 코뿔소)이라는 자바스크립트 엔진이 들어가 있다. 서버사이드 랜더링을 위해 Java8에 기본 탑재된 자바스크립트 엔진을 이용할 예정이기 때문에 1.8이 필요하다. 물론 JDK 1.7 이하 버전에서는 rhino(라이노, 예도 코뿔소)라는 자바스크립트 엔진이 들어가 있는데 라이노 최신버전도 ES5까지는 지원한다고하니까 될수도 있지 싶은데 테스트는 안해봤다.

서버를 설정한 뒤에 실행해서 http://localhost:8080 에서 확인해보자. 여기까지 안되면 다음을 진행을 못한다.

3. JSP 로드하기

2번까지 됐다는 전제하에 이제는 JSP 로드를 위해 추가 설정을 해야한다. src/main/resources/application.properties 파일을 열어서 JSP 설정을 추가한다.

spring.mvc.view.prefix: /WEB-INF/jsp/  
spring.mvc.view.suffix: .jsp

다음으로 build.gradle 파일을 열어서 JSP를 불러올수있는 서블릿 모듈과 jasper 모듈을 추가한다.

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
    compile("javax.servlet:jstl")
    compile("org.apache.tomcat.embed:tomcat-embed-jasper")
}

그리고 잊지말고 꼭 그래들 빌드를 실행하자! 기본 SpringBoot 라이브러리에는 jasper가 기본으로 탑재되어 있지 않기 때문에 빌드를 실행해서 라이브러리를 땡겨오지 않으면 jsp 인식이 안된다. 요것때메 몇시간 삽질한 아픔이….OTL….

오늘은 여기까지
2편에서는 별도의 프로젝트로 리액트 샘플을 만들어본다.