마이갤러리 개발 삽질기 – 2) AJAX는 축구팀이 아니다

사실 이번 미니프로젝트를 기획하며 가장 주안점을 둔 사항 중 하나는 "JS와 친해지기" 그 중에서도 "AJAX 기법과 친해지기" 였다.

AJAX는 "Asynchronous JavaScript and XML" 의 약자로 비동기적인 웹 애플리케이션 제작을 위한 웹 개발 기법이라고 한다. 우리나라 개발 커뮤니티에선 "에이작스" 라고 읽는 것이 보편적인 것 같고 아주 가끔 "아작스" 랄지 "아약스" 라고 읽는 분 있다.

기술 이름이 참 거창하다..... 참고로 내가 옛날부터 좋아하고 잘 아는 Ajax는 아래 사진과 같다. 

마! 암스테르담 등킨 드나스 무 밨나!

죄송하다.

AJAX를 만든 분이 해당 축구팀과의 매칭을 노리고 이름을 지은 것 같진 않지만...AJAX라는 명칭은 내가 생각하는 신박한 IT기술 네이밍 중 하나이다. 

사용 분야는 방대하다. 아마 우리가 요즘 살면서 하루라도 AJAX 기술을 경험하지 않고 살기는 정말 어려울 것이다. 네이버 야구 생중계창에서 자동으로 스코어 바뀌는 것, 그것이 바로 에이작스다. 페북 댓글 단다고 새로고침 되지 않는 것, 그것이 바로 에이작스를 통한 웹 앱 구현이다. 

백엔드 개발을 하며 AJAX를 위한 API개발을 하지 않는 경우는 거의 없다. 더구나 지금 내가 기획한 것처럼 CRUD기능이 있고 회원가입 등 폼에서 중복 방지 로직을 구현하려면 AJAX의 적용은 선택이 아닌 필수가 된다.

 

AJAX 실행의 흐름

간단하다. 웹페이지 상의 특정 이벤트(문자열의 입력, 버튼의 클릭, 화면 아래 끝까지 스크롤을 내리기 등)를 받았을 때 미리 정의해놓은 스크립트가 실행되며 XMLHttpRequest를 서버로 전송하고, 그 요청에 대한 response를 받아와 다시 스크립트 상에 정의된 바에 따라 웹페이지를 통해 사용자에게 노출시키는 것이다.

옛날부터 사용되었던 XMLHttpRequest, 즉 XHR이라는 명칭이 즐겨 사용되고 있지만 최근 AJAX 구현에서 request와 response는 JSON(JavaScript Object Notation)을 사용하는 것이 일반적이다. 이는 JSON의 포맷 자체가 몹시 가벼우면서도 인간도 이해하기 쉬운 직관성을 갖고 있기 때문이고, 그냥 다른 사람들이 많이 써서 그렇기도 하고, 무엇보다 jackson 등의 의존성을 통해 POJO에서 JSON으로의 변환이 무척 빠르고 쉽기 때문이기도 하다.

그래서 AJAX를 구현하고 실행시키기 위해서는...

  • Script가 필요하다. 외부에 정의된 .js 파일이든 웹페이지 내의 스크립트 영역이든 스킙트가 정의되어 있어야 한다.
  • 백엔드 로직이 필요하다. 이 로직은 적당한 JSON 응답을 돌려줄 수 있어야 한다.
  • 통신을 시작시킬 이벤트를 구상해야 한다. 버튼을 클릭하게 할 것인가? 폼을 채워넣게 할 것인가? 심지어 일정 주기로 계속 AJAX 요청을 날리게 짤 수도 있을 것이다(이 기법을 "에이작스 폴링" 이라고 하는데 1단계에선 다루지 않는다).

<마이갤러리> 에서의 적용.

마이갤러리 프로젝트에선 다음의 분야들에서 AJAX 통신을 적용했거나, 적용할 예정이다. 

  • 회원가입 시, 아이디 중복의 체크. 아이디를 입력받음과 동시에 서버에 XHR을 날려 중복 여부를 실시간으로 체크한다.
  • 카테고리 생성 시 카테고리명 중복의 체크. 개념은 상동.
  • 방문자 초대링크 생성. 버튼을 누르면 짠 하고 새 링크가 뜨는 개념.
  • 추후 구현할 메시지 수발신 로직 구현 시에, 받을 회원 이름을 자동완성시켜주는 로직(구현예정).
  • 일정 주기로 서버에 접속해 새 메시지가 있는지 여부를 체크(구현예정).

 

아이디를 입력하다가....
중복이 발생돼 경고창을 보여줬다.

시행착오 끝에 이제 AJAX에 대해 조금이나마 이해하게 된 바, 그 눈물나는 삽질기를 여러분과 차근차근 공유해 보고자 한다. 

회원가입 폼

회원가입 폼에서 AJAX의 역할은 단 하나 - 중복된 아이디로 회원가입이 이뤄지는 것을 프론트단에서 막아내는 것이다.  우리가 흔히 누르는 [중복 확인] 버튼을 구현하는 개념이라고 생각하면 쉬울 듯하다.

버튼을 누르는 방법도 충분히 괜찮지만, 그냥 입력하기만 해도 자동으로 판정이 떨어지는게 더 힙하다(...)고 생각해서 사용자가 한 칸 한 칸 아이디를 채울 때마다 에이작스 통신이 일어나도록 기획하였다.

그런데 칸이 비어있거나 아이디가 너무 짧은데도 에이작스 통신이 일어나는 것은 낭비라고 생각했다. 칸이 비어있다면 아직 입력하지 않은 것으로 간주해 어떠한 동작도 하지 않고, 아이디가 다섯 글자 이하라면 좀 더 입력하라는 메시지를 프론트단에서 노출하고 서버로는 어떠한 통신도 이뤄지지 않게 설계하였다.

비밀번호에 대한 중복체크는 (당연히) 구현하지 않았고 다만 6글자 이상을 암호로 사용하도록 설계했다. 아이디가 중복되지 않으면서 비밀번호가 6글자 이상인 경우 회원가입 버튼을 활성화하도록 구현하였다.

아래는 회원가입 폼을 위한 자바스크립트 코드다:

 join.js


간단한 코드인데 쓸데없이 엔터만 많이 쳐 놔서 길어보이는 거다;;

원리는 정말 간단하다. 아래에서도 설명하겠지만 아이디 입력 폼에서 텍스트 입력 이벤트가 일어날 때마다 checkId()를 실행시킬 것이다.

아래는 글자가 한땀한땀 입력될 때마다 상기한 스크립트가 실행되게끔 설계한 HTML 폼이다.

JOIN.HTML


checkId()는 서버와의 AJAX 통신을 수행한다. 데이터는  HTTP POST로 던질 것이고, 서버는 중복 여부를 unique라는 boolean 오브젝트에 담아 돌려줄 것이다.

만약 입력한 아이디가unique하다면(중복이 일어나지 않아 서버가 unique = true를 반환했다면) 전역 변수로 선언한 idCheck에 true가 입력될 것이고, 아이디 중복 경고창은 jQuery hide 메소드를 통해 사라질 것이다. 마지막에는 setJoinButtonActive()가 실행되어 아이디와 비밀번호 모두 조건을 충족한다면 회원가입 버튼을 활성화시킨다.

말로 하니 어렵다;;

백엔드 로직도 한 번 살펴보자.

APICONTROLLER.JAVA

DUPLICATEDINFOCHECKER.JAVA


본래 AJAX를 위한 비즈니스 로직 구현에 있어서, 받은 오브젝트를 그대로 되돌려주는 것이 best practice라고 알고 있다. 즉, 지금 우리의 상황을 보면 회원가입을 통해 Admin 객체를 만들어내고 있기 때문에 에이작스 response로도 Admin 객체를 똑같이 되돌려주는 것이 가장 좋은 구현이란 얘기다.

그러나 지금의 케이스에 똑같이 적용하기에는 무리가 있다. 우선 우리는 회원가입 과정 중 하나인 "아이디 중복 체크" 를 만들고 있기 때문에 논리적으로도 아직 Admin 객체가 만들어지지 않은 상태다.

또한 회원가입 폼에 구현된 AJAX의 응답으로 Admin 객체를 반환한다면, 그 안에 담겨있는 암호를 비롯한 각종 회원 정보가 불필요하게 한번 더 전송되는 취약점을 갖게 된다.

따라서 나는 AJAX response를 위한 별도의 클래스를 구현하는 것이 맞다고 판단했고 그 결과가 DuplicatedInfoChecker 다. 이름이 맘에 들지 않는다. 이런 경우에 사용하는 컨벤션이 있는지 알고 싶다. 

ApiController에는 @RestController 어노테이션을 붙여놓았다. 이를 통해 리턴값이 POJO가 아닌 JSON으로 반환될 수 있게 구현할 수 있다. POJO에서 JSON으로의 변환은 com.fasterxml.jackson에서 담당하게 된다.

회원가입 창에서의 AJAX 구현 삽질은 이것으로 마무리됐고, 다음 포스팅에서 초대링크 생성 과정에서의 AJAX 적용 삽질기를 공유해보고 싶다.

public static void main (String args[]){
System.out.println("hello, wordpress!");
}

마이갤러리 개발 삽질기 – 1) 나는 왜 이걸 만들고, 어떻게 만들 셈인가 + 1단계 완성 회고

코드스쿼드에서의 과정을 마치고 집에서 빈둥거리던 참이었다. 뭐라도 하지 않으면 배운 것을 곧 까먹겠다는 불안감이 몰려왔다.

배운 것을 복습하는 건 성실한 학생의 기본이지만 나는 사실 성실한 학생이 못 된다. 초등학교 시절부터의 유서 깊은 전통이다. 그럼에도 운이 좋아 노력한 것에 비해 늘 좋은 성과를 거뒀고 주변인들은 이런 나를 보고 “머리가 좋다” 며 칭찬하지만, 사실 머리가 좋다는 말 자체가 그리 좋은 칭찬이 못되는 데다가 나는 내 머리가 그리 좋은 편이 아님을 잘 알고 있다.

그간 잊고 살아도 상관 없었던 학교에서의 수많은 쓰잘데기 없는 배움들을 머릿속에서 비워낸 건 오히려 좀 자랑스럽게 생각하지만, 코딩이야 앞으로 내가 밥을 지어먹고 살 도구로 삼을 예정이므로 복습은 물론 심화 학습을 할 필요가 있었다.

그래서 시작했다. 이런 걸 만들기로….

포부는 거창해야 제맛….

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

진지하다.

코드스쿼드 박재성 선생님의 과정 회고 슬라이드를 보면서 내가 지난 6주간 학습한 내용을 다시 한 번 정리해 보았다:

  • Spring Boot 시작하기
  • Spring JPA, MySQL 맛보기
  • 원격 환경 배포하는 경험, 특히 AWS를 중심으로(사실 과정 전에도 수도없이 했던 것.)
  • TDD, 냄새 안 나는 코드를 향한 집념. 특히 TDD와 유닛 테스트에 대해 배운 것은 정말 큰 배움이었다. 
  • “동의되지 않는 권위에 굴복하지 말라” 사실 동의되는 권위가 별로 없어서 문제…. 

그리고 내가 구상한 마이갤러리의 기술적 요구사항, 그리고 단계별 구현 계획을 구성해 봤다.

마이갤러리가 사용할 기술.

  • Spring JPA + Java Persistence를 활용한 기본적인 CRUD.
  • Amazon S3 + AWS Java SDK를 활용한 오브젝트 관리.
  • AJAX. (AJAX polling 포함)
  • Spring Security + Social
  • WebSocket(Probably? 언젠가 실시간 메시징을 이걸로 구현해보고 싶다)

단계별 구현할 기능

1단계.
  • 사진 / 카테고리 CRUD. DB 기반 회원정보관리
  • AJAX 기반 파일 업로드.
  • 세션 기반 사용자 초대 기능.
2단계.
  • Spring Security를 이용한 회원 정보 관리 + Social Login 클래스 활용.
  • AJAX 폴링 기반 푸시알림.
  • Amazon S3 upload tracker stream을 이용한 파일 업로드 progress bar 구현. 이건 어떻게 해야 할지 정말 감도 안 온다 ㅋㅋㅋㅋ

썩 만족스럽지는 않지만 지난 2주간의 삽질로 1단계는 그 구현이 완전 성공하였다.

http://mygallery.ap-northeast-2.elasticbeanstalk.com/

실제 사진이 어떻게 표시되는지를 보고싶다면 아래의 초대 링크를 눌러보면 된다: mygallery.ap-northeast-2.elasticbeanstalk.com/invitation/wpfoipdkr

노파심에 드리는 말씀이지만 지금 서비스는 완전한 개발 허접이 개발로 개발한 수준이고 보안과 DB 운영이 모두 눈뜨고 봐 주기 어려운 수준이다(인메모리 H2를 사용중이다). 실제 회원가입을 한 후 이 서비스를 사진 공유용으로 사용해보려는 분이 없기를 기대한다. 아울러 모든 정보는 평문으로 전송되고 있음을 유념하시기 바란다.

그래도 2주 삽질해서 이런 걸 만들어냈다.

“개발은 원래 작게 시작하는 것” 이라고 많은 전문가들이 이야기한다. 몇 안되는 동의되는 권위 중 하나이므로 그들처럼 나도 아주 작게, 그리고 단순하게, 지난 6주간 배운 것들을 즐겁게 복습하며 만들었다.

즐겁게 출발했기에 앞으로의 고도화 과정도 즐거우리라 확신한다.

1단계 구현 회고.

긍정 평가
  • 처음으로 제공받은 템플릿도 없이 스스로 부트스트랩 예제를 뒤지고 CSS, JS 파일을 만들어가며 하나의 작동하는 웹 서비스를 배포하는 경험을 했다. 임기응변에 능한 나의 강점을 잘 발휘했다.
  • AWS 파일 업로드를 구현하며 AWS IAM roles에 대해 심화학습할 기회를 가졌다.
  • 매일 조금씩 나아가는 모습을 보였고, 큰 그림을 그려놓고 세부적인 부분을 더해가는 꼼꼼함이 좋았다. 이 과정 속에서 정해진 납기 (9월 17일, 지난 일요일)를 정확히 지킬 수 있었다.
  • AJAX에 대해 많은 공부를 했고, 이제 당황하지 않고 클라이언트 사이드와 백엔드 모두 원하는 내용대로 구현해볼 수 있는 수준이 됐다.
부정 평가.
  • 작업 우선순위의 설정에 대해 더 깊은 고민이 필요하다. 지금처럼 프론트-백엔드를 모두 구현해야 하는 프로젝트에 임할 때, 프론트가 먼저인가? 백엔드가 먼저인가? 알 수 없지만 지금처럼 프론트와 백을 왔다갔다하며 개발하는 것은 좋지 않다.
  • 완전한 TDD를 하지 못했고 유닛 테스트를 게을리했다. 아직도 Model 클래스의 구상을 잘 못하고 있다. Model 클래스와 Domain 클래스의 역할에 대한 더 깊은 공부와 고민이 필요하다.
  • 상기한 TDD를 게을리한 것의 대가로 만족스럽지 않은 코드를 얻어야했다. 기능 구현에 충실하여 목표했던 기능은 다 이루었으나 깔끔한 코드라는 인상을 받지 못한다.
  • 남의 JS 라이브러리 등을 참고했으면, 내가 무엇을 어디서 보고 어떻게 참고했는지를 정확히 기록해 두는 것이 좋겠다. 남들이 “이거 어디서 보고 따라했어요?” 라고 물으면 바로 대답해줄 수 있어야 한다. 그래야 나도 복습이 용이해진다.

-1단계까지 구현한 현재 코드는 https://github.com/seulgiwendy/yoojingram 에 올라가 있습니다. 


public static void main (String args[]){
System.out.println("hello, wordpress!");
}

R 공부하기- 1) AWS EC2, AMI, GitHub

기대를 갖고 수강신청한 전공 수업 "온라인데이터커뮤니케이션"의 사용 언어가 Python에서 R로 변경되었다. Python은 이미 데스크탑에 IDE를 갖춰 놓았고, iPad에도 거금 10.99$를 투자하여 Pythonista라는 IDE 비스무리한 애플리케이션을 설치해둔 터였다. 무엇보다 Python은 코드잇 선생님들의 지도 하에 기초를 꽤 닦아둔 언어여서 갑작스러운 변경이 당혹스러울 수밖에 없었다. 

새로운 언어를 배우는 것은 물론 내게 도움이 될테니 꺼려지는 변화는 아니겠지만, R언어를 사용하기에 마땅한 iPad 애플리케이션이 아무리 찾아봐도 없다는 게 문제였다. 물론 가장 좋은 방법은 짱짱한 노트북을 한 대 장만해 학교에 들고다닐 수 있는 개발 환경을 구축하는 것이겠지만, 이 수업 하나 듣자고 딱히 필요성도 느끼지 못하는 노트북을 구입할 수 없었다. 이미 왠만한 일은 iPad로 클라우드상에서 끝내고 있기 때문에..... 

구글에 무작정 "R언어 아이패드" "아이패드 R코딩" "R on the ipad"와 같은 쿼리를 던져넣었고, R의 IDE인 Rstudio가 Web에서 접근 가능한 서버 형태로 배포되고 있다는 사실을 파악했다. 그리고 단숨에 떠오른 것은 아마존의 클라우드 서비스 AWS, 그리고 EC2였다. 

이 로고를 처음 보는 사람은 거의 없을 것이다.

AWS는 물론 종량제 클라우드 서버지만 감사하게도 처음 사용자용 Free Tier를 제공하고 있고, 이 요금제는 언제나 실행 가능한 ubuntu 서버를 포함한다. 

오랜만에 리눅스 명령어 좀 치겠구만....이라고 생각하던 차에, 또한 구글링으로 아래와 같은 블로그를 발견. 

능력자는 도처에 있다.

http://www.louisaslett.com/RStudio_AMI/에서는 이미 다 세팅된 아마존 가상머신 이미지(AMI:Amazon Machine Image)를 배포하고 있었다. 정확히 말하면, 해당 AMI가 AWS Marketplace에 등록되어 있다. 

평소 미국에서의 삶을 동경하는 나는 내 서버만이라도 북가주에 둬 볼까 하고 N, California를 클릭할 뻔 했으나, 사용중인 LG U+망의 극악무도한 해외망 접속 속도를 떠올리고 아쉬움을 잔뜩 머금은 채 Seoul Region을 선택할 수밖에 없었다. 

저 링크를 누르기 전에 미리 AWS 계정을 만들어 두는 것이 좋다. Amazon 계정이 있다면(직구를 한번이라도 해본 적이 있다면) 해당 account에 AWS 서비스가 등록될 것이고, Amazon 계정이 없다면 이 참에 새로 하나 만들면 그만.

AMI를 내 EC2에 설치했다. 설치하는 과정은 물 흐르듯 자연스러워서 스크린샷을 찍거나 설명을 참고할 것 조차 전혀 없었다. 내 데스크탑에 오리진 게임 설치하는 거보다 쉬웠음. 난생 처음 만져보는 클라우드 서버라고 바짝 쫄아 있었는데 민망하게 됐다. 

보안 그룹? 보안 군집? 뭐라 번역을....

딱 하나 신경써줄 부분은 생성한 EC2 Instance에 http로 접근할 수 있게 보안 그룹 설정을 만져줘야 하는 것이다. 해당 AMI로 배포되는 R Studio 서버는 HTTP 80번 포트를 사용한다. 80번 포트까지 모든 포트가 열려있도록 Inbound 설정을 하면 된다. SSH도 열어줘야하는데 아마 default로 열려있을 것이다. 

모든 설정을 마치고 생성한 Instance의 Public DNS로 접속하면 아래와 같은 R studio 로그온 창이 잘 보일 것이다. 기분이 매우 좋다. 

로그온 화면. 기본 ID/PW는 rstudio/rstudio임.

이제부터 이 콘솔에서 수행하는 모든 작업은 EC2가 연산하고, 해당 EC2에 귀속되어 있는 EBS에서 데이터를 저장할 것이다. 기본 저장용량 세팅은 10GB로 되어 있는데 한학기간 수업을 수강하는 데에 아마 충분하고도 남을 거라 본다.

Web 기반이니까 머신에 관계없이 HTTP페이지를 탐색할 수 있는 기기라면 어디서나 이 R studio에 접근할 수 있다. 당초 이 삽질(?)을 한 목적이었던 iPad에서 스튜디오에 접근해 보았다.  

잘 된다.

보시는 것처럼 노트북 살 돈을 굳혔음을 알 수 있다. 

어차피 모든 작업이 클라우드 상에서 연산되고 기록되므로 GitHub Integration은 딱히 필요없을 것 같기도 했지만, 그래도 그때그때 배운 내용을 노트해 둔다는 의미가 있으므로 GitHub 연결을 수행하기로 했다. 이 과정이 의외로 까다로웠다. 

우선 R Studio에서 내 리파지토리로 접근할 수 있게 public key를 추가시켜주는 과정이 필요하다. R Studio에서 발급한 public key를 GitHub에 등록시켜두면, R Studio에서 그 키를 제시해서 내 리파지토리에 코드를 푸시하는 방식이다. 말이 어려운데 그냥 현관문 디지털 도어락에 암호 설정하는거랑 똑같다 말만 그런게 아니라 원리가 그거랑 똑같음. 

GitHub Integration 과정 전반은 이 포스팅을 보고 따라했다. 

여기서 키를 만들고,
여기서 등록한다.

shell을 두려워하면 안된다. 아주 간단한 코드 몇 줄을 치고 나니 R Studio가 그간 commit됐던 변경 내용들을 리파지토리에 푸시하기 시작한다. (프로젝트를 만들 때 Version Control을 사용하겠다고 체크해둬야 함.) 

벨덕이라서 죄송합니다.

코드 달라고 열심히 remote repository 접근법을 알려주던 내 OnlineDataComm 리파지토리가 R 코드를 받아먹고, R언어의 리파지토리임을 파란색으로 나타냈다. 여담이지만 저 언어별 색깔 커스터마이즈할 수 있는 것인지 궁금. 자바가 9호선색(...)이라니 영 어울리지가 않아서...... 

샘플 코드라도 몇 개 올려놓고 스샷을 올리고 싶지만 R은 전혀 몰라서 (hello world도 모름) 그럴 수 없는 사정을 양해해 주시기 바란다. 

오늘 배운 점

  • iPad R 코딩 환경(혹은 어디서나 접속할 수 있는 클라우드기반 개발 환경)을 구축하기 위해 AWS 인스턴스를 세팅하는 방법.
  • R Studio에서 GitHub 연결을 수행하는 방법.
  • 코딩한다는 핑계로 노트북을 굳이 살 필욘 없단 사실.  

더 알고싶은 점

  • 헬로월드라도 할 줄 알았으면 좋았을걸.....
  • AWS Instance는 종량제 과금인데, Instance를 Free Tier 한도 이상 사용했을 때 내게 알림 없이 바로 과금이 시작되는 건가? 

리빙 포인트

  • 노트북 없이 코딩 수업을 수강하려면, 클라우드 서비스를 적극적으로 알아보는 것이 좋다. 
  • 아이패드는 한번 사놓고 두고두고 뽕을 뽑아 갈아 마시는 것이 좋다.