메일을 맡을 때 아무리 그래도 내가 못 하는건 아니겠지 라는 생각이었는데 메일에 대한 정보를 모으는데만 일주일 정도 걸렸던 것 같다. 서버를 만들어야 하는 것은 이미 알고 있었지만 그 서버를 어떻게 만들어야 하는지 서버를 만들고 어떻게 적용 시켜야하는지 다른 사람들은 어떻게 코드를 작성하는지 다 처음하는거였기 때문이다. 그나마 다행인 것은 학원에서 회원가입 부분을 배울 때 JAVA를 통해 Gmail로 이메일 본인 확인을 배운 적이 있어서 메일 전송 부분의 코드는 알아볼 수 있었다. JAVA 메일은 대부분 Apache James를 이용한다는 정보는 쉽게 알았지만 사내메일에서 그것을 어떻게 활용하는지... mailContainer의 mailet 셋팅을 어떻게 하는지 정말 어려웠다. 구글링을해도 생각보다 한국인이 쓴 정보가 없었기에! 결국은 Apache James의 홈페이지 상세 안내를 번역기를 돌려 하나하나 읽어보고 내 입맛에 맞추어서 셋팅했는데 왜 강사님이 시간이 지나면 사람들이 쓴 글 보다 그냥 사이트에서 찾아보는게 더 낫다고 했는지 깨닫게 되었다.
James의 사용법에 대해서는 따로 포스팅을 할 예정이지만 내가 고심했던 부분을 간단히 서술하자면 일단 처음에는 DB연결이였다. James의 초기 셋은 Derby로 되어있기에 명령 프롬프트로 밖에 접근이 불가하다. 그것을 눈에 쉽게 보기 위해서 내가 사용하는 Oracle로 전환해야 했는데 어떻게 전환하느냐! 그리고 계정 생성과 삭제 명령을 자바에서 어떻게 사용해야하는가..
생각보다 전자는 쉽게 해결되었다. JAVA의 DB연결과 똑같이 url, driver, username, password를 적용하면 되었기 때문이다. 하지만 후자의 경우 아무리 검색해도 나오지 않았다. 어떻게 해야할지 한참 검색창을 돌리다가 DB연결도 JAVA연결과 똑같은데 그냥 쿼리문을 내가 작성하면 되지 않을까 싶어서 테스트를 해보았는데 그게 성공적이었다. 알고보니 James는 JPA 기능이 내재되어 있어 내가 설정한 DB에서 실행할 경우 알아서 테이블과 데이터를 생성해 주었다. 그 JPA가 똑같이 DDL를 사용하기에 내가 직접 DDL을 작성하면 되는 것이었다! 그 뒤로 계정 생성, 삭제는 술술 풀렸다. 나는 JPA를 사용하지 않는 JAVA 웹 개발을 했기 때문에 xml Mapper에 직접 Insert와 Delete쿼리를 작성해 사원을 생성하고 삭제할 때 사원번호를 바탕으로 사용할 수 있게 했다.
다음의 난관은 SMTP, POP3, IMAP의 Mail Protocol이었다. 셋 다 메일 관련 정보들을 거의 일주일 내내 찾아봤기에 머리에 들어가 있고 어느정도 이해가 되었으나 실제로 사용할 때 사용이 안된다는게 큰일이었다. 물론 SMTP와 POP3는 잘 되었지만 IMAP이 진짜진짜 되지 않았다.
대부분의 포스팅이 James 설치, 실행 그리고 계정생성을 마친 후 확인을 위해 윈도우에 내재되어있는 telnet을 사용하여 James를 통해 생성된 포트로 메일 전송과 메일 가져오기를 실행해보라고 한다. 명령어는 telnet localhost(또는 내가 생성한 도메인) 25(SMTP), 110(POP3), 143(IMAP)인데 SMTP와 POP3는 실행했지만 IMAP이 죽어도 안되었다. 나중에 개발이 한참일 때 설정을 이것저것 만지고나서는 IMAP이 또 실행되었지만 왜 그게 안되었는지는 아직도 모르겠다. telnet의 단점은 내가 작성하다가 오타가 나서 지울 경우 그것도 다 입력이 되어있는지 틀린 명령으로 받아들이는 것이다. 처음에는 이유도 모른 채로 몇 번을 다시 했는지 모르겠다.
하지만 내가 SMTP를 통해 메일을 전송하고 POP3로 보낸 메일을 확인했을 때 뿌듯함이 아주 컸다. 서버를 구축했다고 보기는 조금 어렵지만 첫 발을 내딛었구나 어느정도 했구나 하는 그 감동에..ㅜ 강의를 듣고 한 것이 아닌 구글링을 통해 나 혼자 이런 것을 해내는게 스스로 기특하기도 했고 옛날에 이렇게 혼자 낑낑거리면서 게임을 돌리거나 스타의 유즈맵, 츠쿠루RPG 같은걸 만들거나 했던 기억이 떠올라 어릴 때로 돌아간 것 같았다.
사실 이건 사용해보았다의 축에도 끼지 않겠지만..(물론 James도 마찬가지) 여러 포스팅에서 마찬가지로 Thunderbird를 통한 Mail 전송 확인을 게재했기에 내가 제대로 만든것인지 확인하기 위해 사용해보았다. James등의 기본 Mail 서버 구축이 끝났다면 사용방법은 쉬웠다. 만들어둔 계정으로 SMTP, POP3, IMAP 포트에 연결하여 일반 메일처럼 사용하는 것이었다! 물론 내가 실제 도메인을 가진 상태이고 SSL 인증서 등을 받아야 구글이나 네이버같은 대형 포털에 메일 송수신이 가능하기에 그냥 내부 계정 안에서 메일 송수신을 테스트했다.
여기서 또한 IMAP이 문제였다. 이놈의 IMAP은 대체 왜 안되는건지 모르겠는데 Thunderbird에서 IMAP포트로 로그인 시 패스워드 확인 무한 루프에 빠져서 계정로그인이 불가했다. ㅜㅜㅜ 설정을 이리저리 바꾸고 나서는 집에서 IMAP로그인이 가능했으나 학원에서는 또 안되더라... 학원에서 첨부파일 메일 전송도 Socket write error가 자꾸 떠서 구글링을 통해 jdbc.properties 파일에
url="jdbc:mysql://localhost:3306/계정?autoReconnect=true
jdbc.validationQuery="SELECT 1"
jdbc.testOnBorrow="true"
추가도 해보았지만 학원에서 사용하는 서버 측에서 원래 막혀있는 것 같았다.. 흑흑 왜 그렇게 생각했냐면 우리 집에서는 잘만 되는데 학원에만 오면 안 되었기 때문이다! 아무리 설정을 바꾸고 난리를 쳐도 안되었다. 쿡.. IMAP 로그인도 안되고 첨부파일 전송도 안되다니. 물론 학원에서 가르치는 과정에 Web 개발이 중심이지 서버 구축은 들어가 있지 않기에 그럴 수 있다 생각했다.
여튼 Thunderbird에서도 로그인과 메일 송수신이 이루어지는걸 확인했고 이제 진짜 진짜 코드 개발에 착수했다. 참 웃기게도 진짜 코드 개발을 프로젝트 시작하고 10일이 넘어서 시작한 것 같다.
코드를 배울 때도 느꼈지만 이번에 배우지 않은 코드를 작성하면서 크게 와닿은 사실이 있다. 이 코딩 언어를 만들고 언어를 개발 시키고 언어를 이용해 무언가를 창조해낸 사람들은 정말 천재가 아닐까... 아니 그냥 사람이 아닌게 아닐까? 그도 그럴게 이번 프로젝트에서 컨닝을 하였느냐고 누가 묻는다면 입이 바싹 마른 채로 맞다고 나는 그리하였다고 진실을 고할 수 있으니 말이다.
일단 제일 많이 참고한 블로그는 forest71님의 티스토리이다. 이분께서 공부하는 사람들을 위해 배포한 그룹웨어 프로젝트를 다운 받아서 메일 부분을 분석하고 뽑아썼다. 정말 감사하게도 image, attach, content를 MimeBodyPart에 맞게 변환 시키는 메소드를 아주 잘 사용했다. 이렇게 쓰고보니 핵심 부분을 다 가져다 써서 너무 찔린다. 물론 이해는 다 했다! 다른 사람의 코드를 가져다 쓰면서도 왜 이 클래스를 사용하는지 왜 이 부분에 넣는지 왜 사용해야만 하는지 이해하는 것을 먼저했기 때문이다. 그렇지 않으면 나중에 내 코드를 다시 볼 때, 남이 물어볼 때, 또 사용해야 할 경우에 사용하지 못 하고 대답하지 못 하는 때가 생기기에 그것만은 꼭 했다. 그리고 사실 그대로 배꼈다기 보다 짜집기 형식으로 코드를 가져와서 이해하지 않으면 쓸 수 없었다.
그나마 다행인 것은 ServiceImpl에 거의 모든 코드가 들어가는 방식으로 배운 나와 다르게 Controller와 Service에 코드를 나누어서 쓰고 SqlSession을 활용하는 방식이어서 저런 Util에 가까운 메소드를 제외하고는 다 직접 썼다는 것이다. 물론 뷰 페이지도 내가 원하는 것과 달라서 거의 직접 만들었다. 사실 SMTP는 사용했으나 IMAP은 사용하지 않았기에 CRUD에 가까운 형식이어서 코드 짜는데 두뇌가 많이 필요하지는 않았다.
그럼 어째서 IMAP을 쓰지 않았는가? 물론 사용하면 좋겠지만 내가 메일을 다루는 회사 혹은 서버를 다루는 그쪽 계열로 지원할게 아니라면 기본 CRUD를 얼만큼 배워서 얼만큼 활용할 수 있는지 보여주는게 더 낫겠다는 생각이 들어서였다. 어쨌든 나는 JAVA Web 개발자로 일할 것이기 때문에 쇼핑몰을 만들든 뭘 만들든 기본적인건 다 할 수 있습니다 라는걸 보여주고싶었다.
그렇게 쉽게 만들 수 있을거라 생각했는데 이 사람 저 사람 코드, 조원이 찾아온 전 기수의 그룹웨어 깃허브 코드를 보니 다들 AJAX를 많이 사용하는 것을 알게 되었다. 대체 왜 굳이 AJAX를 사용하는 걸까 고심해보니 그럴만 했다. 읽음처리, 다중삭제, 메일 전송 내역 등이 실시간으로 이루어지고 보여져야 하기때문이었다. 그것도 굳이 이 페이지를 떠나지 않고 한 페이지 내에서.. 이럴거라면 Restful한 개발을 하는 것도 좋았을텐데ㅜㅜㅜ 라고 생각했으나 나보다 빨리 프로젝트를 경험한 여자친구가 정말정말 다들 지켜주지 않는다. 쉽지 않다라고 했던게 생각이 나 했어도 못 했겠지라며 단념했다. Restful 또한 심도 있게 배운 것이 아닌 도전해야 하는 것에 가까웠기 때문에 아무래도 조원들이 지켜주기 어렵지 않을까 생각이 들었다.
AJAX의 처리도 물론 손이 많이갔지만 어렵지는 않았다. 다만 Jackson을 사용하여서 Serivce에서 거의 Map으로 반환하고 Controller에 @ResponseBody가 많이 붙어있다는게 깔끔한건지 그냥 초보자라 무식한건지 모르겠을뿐이었다.
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import com.group.sharegram.addr.domain.AddrGroupDTO;
import com.group.sharegram.mail.domain.MailAtchDTO;
import com.group.sharegram.mail.domain.MailDTO;
import com.group.sharegram.mail.domain.ReceiversDTO;
public interface MailService {
public Map<String, Object> saveMail(MultipartHttpServletRequest multipartRequest, MailDTO mail);
public Map<String, Object> getReceiveMailList(HttpServletRequest request, String deleteCheck, String receiveType);
public Map<String, Object> getReceiveMailInfo(HttpServletRequest request, Model model, ReceiversDTO receivData);
public Map<String, Object> changeRead(List<String> mailNo, List<String> readCheck, HttpServletRequest request);
public Map<String, Object> moveInTrash(List<String> mailNo, String receiveType, HttpServletRequest request);
public Map<String, Object> saveSummernoteImage(MultipartHttpServletRequest multipartRequest);
public ResponseEntity<Resource> download(String userAgent, int fileNo);
public ResponseEntity<Resource> downloadAll(String userAgent, int mailNo);
public List<MailAtchDTO> getMailAttach(int mailNo);
public Map<String, Object> deleteReceiverData(List<String> mailNo, List<String> receiveType, HttpServletRequest request);
public Map<String, Object> restoreReceiverData(List<String> mailNo, List<String> receiveType, HttpServletRequest request);
}
실제 작성한 Service인 이것처럼 말이다..
메일 전송 코드를 거의 다 만들고 나서 깨달았다. 첨부파일 전송을 내가 뒤늦게 넣었는데 그게 실수였다. 끼워넣으려고하니 수정해야 할 코드가 한 두개가 아니었기 때문이다. Summernote라는 편집기 또한 중간에 넣어서 MimeMessage에 Mail 정보를 담아 보내던걸 MimeMultipart로 아예 변환 시켜놓은 상태라 편했지만 그건 본문에 이미지를 보내는거라 cid 처리만 했고 비슷한 다른 코드를 새로 넣어야 했고 남의 것을 보고 배낀 테이블도 수정해야했고 내가 보낸 파일을 다운 할 수 있게 download 메소드 들을 또 만들어줘야했기 때문이다. download 또한 AJAX처리가 필수였다.
그래도 강의에서 자료실 게시판을 배운 적이 있어서 술술 해냈지만 문제는 아까도 말했지만 학원에서 첨부파일 메일이 전송되지 않는 오류였다. 거기에 거의 하루하고 반나절을 붙잡혀있었는데 집에서 같은 환경으로 전송 해보고 SMTP, IMAP, MailContainer 셋팅을 변경하고 telnet, Thunderbird, tomcat 서버를 이용해 전송 테스트를 다 해본 결과 그냥 학원에서만 안되는거였다. 이때에도 아.. 계획했던거 할 때 해야하는구나 괜히 끼워넣고 리팩토링하면 일이 커지고 손 볼게 많아지네 왜 강사님이 리팩토링할 시간 없으니까 돌아가면 일단 그대로 진행하라고 했는지 크게 깨달았다.
받은 메일, 보낸 메일을 가져오는게 쉬울 줄 알았는데 생각보다 헷갈리는게 컸다. 메일 송수신은 계정들이 To, cc, From의 관계로 이루어져있기에 받은 메일은 To, cc만 보낸 메일은 From만 휴지통은 To, cc, From 전부 그리고
(내가 만든 메일 페이지이다.)
이렇게 메일 받는 사람 혹은 보낸 자의 대표 이름이 나와야하기 때문에 나는 Receiver 테이블을 새로 만들어서 그 안에 Mail Type을 지정해주었고 Mail 테이블에 Sender 칼럼을 넣어서 보내는 사람의 이메일을 넣었다. 참고로 저게 다 내부 사원의 메일로 보내서 저렇게 이름이 뜨는거고 외부에서 메일을 보낼 경우 그 메일 주소가 뜬다.
휴지통에서도 타입에 따라서 받은 메일은 보낸 사람이 뜨도록 보낸 메일은 받는 사람의 이름이 뜨도록 만들어두었다.
그 뒤 읽음, 답장, 전달, 삭제 등 자잘한 부분까지 다 만들고 메일은 일단 개발을 종료했다.ㅜㅜ 왜냐면.. 주소록도 해야하고 조원들것도 봐줘야했기때문에 내 것을 더 개발할 시간이 없었다.