마이갤러리 개발 삽질기 – 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!");
}