- 맛.JAVA 팀은 맛집 탐방에 누구보다 진심인 사람들이 뭉친 팀입니다. 🍔
- 평소에 모두가 겪고 있던 부정한 광고, 믿을 수 없는 후기 속에서 소비자들이 믿고 방문할 수 있는 맛집을 모아 볼 수 있는 사이트의 필요성을 느꼈습니다.
- 그래서, 영수증 2회 이상 인증된 맛집만 등록되도록 해서 신뢰도 및 만족도가 높은 맛집만 선별하여 소비자에게 제공하는 목적으로 개발을 진행했습니다.
- 국내 운영 중인 맛집 추천 사이트, 대형 포털 지도 사이트의 사례 분석을 통해, 웹사이트 기능의 방향성을 "진정성 있는 맛집 공유"로 초점을 맞췄습니다.
- 맛집을 좋아하는 사람들 뿐만 아니라, 맛집을 좋아하는 사람들의 방문을 원하는 요식업계 사장님들도 타켓팅한 사장님 전용 구독 서비스 및 노출 배너 광고를 BM으로 설정했습니다.
- Java 8
- Spring Framework 5.0.1, Spring MVC
- Maven
- Mybatis
- Eclipse
- HTML
- CSS
- JavaScript
- JQuery 3.6.4
- BootStrap 4.1
- MySQL 8.0.32
- Git, Sourcetree
- Slack
- Notion
- Naver, Kakao Social Login
- CLOVA Chatbot API
-
- 이메일 인증, 유효성 검사를 통한 회원가입
- 일반, 간편 로그인, 임시 비밀번호 발급 가능
- 챗봇 API를 통해 자주 묻는 질문 등록
-
- 영수증, 카드내역을 통한 또 갔던 맛집 등록기능
- 2회 이상 등록된 맛집 검색기능
-
- 음식점 상세정보 CRUD 기능 및 카테고리별 음식점 검색 기능
- 상세페이지 URL 복사(for 공유하기)
- 네이버 포털사이트 이동하여 해당 음식점 검색
- 카카오 API 사용 ► 해당 음식점의 주소를 받아서 지도 띄우기 + 해당 음식점의 위치를 기반으로 카테고리별 장소 검색
-
- 맛집에 관심이 있는 소비자가 이용하는 커뮤니티로, 리뷰 / 사진 / 지유게시판으로 나누어 유저 용도에 따라 세부 메뉴 분류
- 각 게시판별 게시물 CRUD 기능 및 댓글 insert 기능
- 리뷰게시판은 영수증 등록 여부를 체크하여 영수증 등록을 한 유저만 리뷰를 남길 수 있도록 제약사항을 추가하여 리뷰의 신뢰도를 강화함.
-
- 구독 결제 시 사장회원등록, 결제내역 DB 저장
- 결제내역 DB를 기반으로 데이터 선별하여 매출차트 및 재방문 차트 구현
- 리뷰 AI 감정분석 차트 - 사용자들이 작성한 리뷰 데이터를 기반으로 AI 감정분석 API를 사용하여 감정분석 결과를 출력
- 자유게시판 및 좋아요 기능- 게시글 수정, 검색 기능을 강화, '좋아요' 기능을 추가
-
- 영수증 등록 시 포인트 적립
- 적립된 포인트를 사용해서 상품교환(기프티콘) SENS API 사용해서 문자로 전송
- 등록된 영수증을 카운트해서 많이 등록된 음식점 카테고리별 분류
-
- 방문 예정인 맛집 일정을 등록하고, 확인할 수 있는 캘린더 기능을 구현함.
| 회원가입, 로그인, 마이페이지, AI챗봇 | 영수증 등록, 검색기능 |
|---|---|
| 음식점 상세페이지 | 회원 커뮤니티 |
| 사장 커뮤니티 | 포인트 시스템, 랭킹 시스템 |
| 캘린더 | |
📌 핵심 기능 설명
- 핵심 기능:
이메일 인증,간편 로그인,챗봇
- Gmail 설정에 IMAP 액세스 상태를 사용으로 수정 후 앱 비밀번호 생성
- root-context에 이메일과 비밀번호, SSLSocketFactory 저장하여 빈 등록
- 서비스에 랜덤으로 6자리 인증번호 생성 메소드 정의하여 선언한 변수에 저장
- 이메일 폼 작성 메소드에서 폼 작성하여 전송 메소드 호출하여 메일 전송 후 인증번호 리턴
- 이메일 전송 메소드에 작성 폼을 받고, 메시지를 생성하여 입력된 이메일로 전송
- !!결과!! 이메일 인증 버튼 클릭 시 인증번호가 담긴 메일 전송!
- [👉전체 흐름 확인하기]

- 활용 UUID를 생성하여 Low time 부분만 변수 선언 후 임시 비밀번호로 update하여 메일 전송
- [👉전체 흐름 확인하기]

- 로그인 API 사용시 필요한 값(클라이언트 아이디, 시크릿 키, 콜백 URI, 세션 상태, 프로필 URL)을 변수 선언
- BO에 1. 토큰 메소드, 2. 인증 URL 생성 메소드 정의, 3. 세션에 선언한 상태값을 넣는 getter, setter 정의
- 토큰 생성 시에 코드 정보를 받아 토큰으로 발급
- 컨트롤러에서 콜백 경로로 맵핑 후 버튼 클릭 시 호출되는 메서드 생성
- 토큰 발급 메서드 호출 후 저장된 토큰값을 가지고 유저의 정보 획득
- json으로 넘어온 값을 파싱하여 필요한 값을 dto에 저장
- !!결과!! 회원 조회 후 없으면 자동 회원가입 진행, 있으면 세션에 아이디 저장 후 콜백 url로 리턴
- 카카오 로그인은 전화번호를 받을 수 없어 마이페이지로 포워딩하여 정보 수정 받도록 진행
- [👉전체 흐름 확인하기]

- TextWebSocketHandler를 상속하는 챗봇 핸들러를 작성
- 핸들러에 1. 웹소켓 연결 후, 2. 클라이언트의 텍스트 받아올때, 3. 연결 해제 후 이렇게 3가지 상황의 메소드 정의
- 핸들러를 servlet-context.xml에 빈 등록, 웹소켓 핸들러로 설정하고 웹소켓 연결 경로 지정
- javascript에서 정한 uri로 웹소켓 객체 생성하고 성공하면 웰컴 메시지 전달
- 유저가 버튼 클릭하여 버튼의 메시지를 발신하면 메시지에 맞는 내용 수신
- javascript로 수신한 메시지 json을 파싱하여 description의 부분이 유저에게 보일 수 있도록 메소드 정의
- 유저가 메인이 아닌 다른 페이지로 이동할 경우 연결 해제
- 결과!! 클로바 챗봇 API에 작성한 시나리오 흐름에 따라 FAQ 진행
- [👉전체 흐름 확인하기]

⚽ 트러블 슈팅
메시지 리턴 과정,간편 로그인 정보 저장,챗봇에 관한 트러블 슈팅
- 유효성 검사를 진행하여 에러가 발생했는데 에러 메시지가 아닌 null 값 출력
- 404, 500 등 클라이언트, 서버의 오류는 없었고 이클립스 콘솔에 뜨는 에러도 없었음
- request에 저장해서 view에 불렀는데, request의 저장된 값의 변수명이 다른가?
- 내가 리턴하는 방식에 문제가 있나?
1. request에서 get하는 키의 변수명 문제 ❌
에러가 있을 시에 서비스에서 key=value 형태로 에러를 저장하는데, 저장한 map을 출력하여 테스트
key에 내가 포맷한 이름 형태와 value에 내가 쓴 에러 메시지가 출력
모델에 넣어서 전달하는 값도 동일하게 출력
=> key의 값을 넣은 request에서 get하는 변수명은 문제가 아니다!
2. 리턴하는 방식의 문제 ⭕️ -> 리턴 방식 변경
문제가 발생했을때의 나는 모델, 즉 request영역에 값을 저장하고
리턴 값에 정한 경로에 리다이렉트를 사용하여 로그인 페이지로 넘김
리다이렉트는 request와 response 객체가 새롭게 생성되는데, 그걸 모르는 상태에서 사용
- 원래 코드 -> 수정 코드
return redirect:/mz_member/login->return /mz_member/login
리다이렉트 VS 포워딩
URL을 다시 가리킨다라는 뜻으로,- 클라이언트가 서버에 요청한 URL을 받고 서버에서 해당 요청이 이동되었음을 확인한 경우
- 이동된 url로 재접속을 응답하여 클라이언트가 이동된 url에 재접속하여 다시 url 요청
- url 확인 후 코드와 body에 html을 응답
- 여기서 리다이렉트 응답은 3xx / 정상 응답은 2xx으로 코드 응답
- 리다이렉트는 재접속을 요청하기 때문에 request와 response의 값은 더이상 유효하지 않음
- 세션 값은 유효함
건내주기라는 뜻으로,- 클리이언트와 통신하여 처리하는 리다이렉트와 달리, 포워딩은 서버 내부에서만 처리
- 클라이언트가 서버에 url을 요청하면 서버에서 내부 처리를 하고 2xx 코드와 함께 응답
- 그래서 클라이언트는 실제로 다른 페이지로 이동했는지 알 수 없음
- 포워딩은 요청정보가 다음 url에서도 유효하기에 request와 response 객체를 공유
- BO의 승인 토큰을 얻는 과정에서 세션 검증용 값과 세션에 저장된 값이 동일하지 않아 null값을 리턴
- 승인 토큰을 얻지 못해서 로그인이 실행되지 않고 500 상태의 오류 발생
- 세션에 값을 정하는 setter에서 잘못된 값을 저장하나?
1. 세션 setter 부분의 문제 ⭕️ -> setter에 들어가는 값을 수정
세션 setter의 값을 출력해보니, 세션에 세션 상태에 고유 식별자 ID를 저장했어야 하는데
코드를 잘못 보고 그냥 받아온 세션 값을 지정함
=> setter부터 잘못되었으니, 잘못된 값을 get해서 승인 토큰 발급 도중 값이 동일하지 않아 null을 리턴했고, 순차적으로 null값이 넘어간 것
- 원래 코드 -> 수정 코드
session.setAttribute(SESSION_STATE, session)->session.setAttribute(SESSION_STATE, state)- SESSION_STATE는 지정된 값을 저장해놨던 값 변수명
당황하지 않고 실행 순서의 코드를 되집어보는 습관!
- 처음 의심갔던 컨트롤러의 승인 토큰 받는 부분을 출력해 보고 콘솔을 확인하니 null값이 발생
- 어디서부터 잘못된 것인지 코드의 실행 순서를 반대로 올라가보니, 경로 비교하는 if문에서 null을 리턴하는 것을 파악
- 이렇게 코드를 실행 순서의 반대로 올라가니 생각보다 빨리 해결할 수 있었음
- 이 경험을 통해서 코드의 실행 순서를 명확히 아는 것이 얼마나 중요한지 알게 됨!
이클립스의 디버깅을 활용
- Debug 모드를 활용하여 break point를 먼저 찾아볼 것!
- 멘토 분께서 알려주신 방법
- 웹소켓 연결에 지정한 경로 값을 찾지 못해서 계속 연결 실패
- handler의 endpoint로 지정한 경로의 문제인가?
- 컨트롤러의 매핑 값의 문제인가?
- 전송된 데이터 자체를 인식 못해 생기는 문제인가? * 이 부분은 개발자 도구를 이용해 어디서 에러가 나는지 확인해 보니 abstract-xhr.js 파일에서 payload 인식을 못하는 것을 파악
1. endpoint로 지정한 경로의 문제 ❌
웹소켓과 연결하는 클래스 파일의 endpoint와 웹소켓 객체 생성하는 메소드의 경로를 출력해서 확인해보니
둘이 동일한 값이 나왔기에 그 부분의 문제는 아니었음.
jstl로 상대 경로를 인식하지 못하는 건가 싶어서 절대 경로로 바꿔도 실패
setAllowedOrigin 부분을 *로 지정해서 모든 값이 되도록 핸들링했는데, 이것도 실패
=> endpoint로 지정한 값과 웹소켓 객체 생성하는 경로가 달라 생기는 문제가 아니다!
2. 매핑한 값의 문제 ❌
매핑 이름이 다르면 항상 이클립스 콘솔에 매핑을 찾을 수 없다는 에러가 뜨기에 아니라는 생각은 가지고 있었음
그래도 값을 변경하여 실행해보았으나 결과는 똑같이 연결 실패
일부러 매핑 값을 다르게 지정해 봤는데,
이때는 콘솔에 매핑한 것을 찾을 수 없다는 에러 발생하여 매핑 문제는 아니란 것을 확신
=> 매핑 값이 달라 생기는 문제가 아니다!
1. 데이터 자체를 인식 못함 ⭕️ -> 프로젝트를 새로 만들어 실행
개발자 도구를 통해 에러가 나는 부분을 확인하고 이 부분 해결을 위해 정보 검색
1-1. 첫번째 시도는 에러나는 부분을 주석 처리하고 실행
다른 사람의 경우, 같은 에러를 봤을때 주석 처리하고 실행하면 에러 없이 된다는 글을 발견
나도 같은 부분을 주석 처리 후 실행 -> 에러는 없었다
그러나! 연결 자체가 안 되었음. 연결된다면 connect가 콘솔에 찍히도록 코드를 작성했지만,
콘솔에 찍히지 않고 연결되지도 않아서 이건 정확한 해결 방법이 아니라고 파악
1-2. 💡 새로운 프로젝트를 만들어서 웹소켓 연결 테스트를 진행하니 해결!
=> 알아본 결과 sockjs-node에서 에러가 났는데, 이것은 서버 자체의 문제
- 개발 중 네트워크 환경 변경으로 서버가 액세스 소스 확인 못한 것
- sockjs-node와 sockjs-client에서 넘어오는 값을 확인하면 어디의 오류인지 알 수 있다!
- sockjs-node는 서버의 오류 / sockjs-client는 클라이언트의 오류
- 너무 오래 잡고 있어도 득이 되지 않고, 정말 모르겠다면 새로운 프로젝트를 파서 해결해보는 것도 하나의 방법임을 깨달았다
- 웹소켓 연결과 챗봇 API의 시나리오대로 웰컴메시지는 뜨지만, 그 후에 버튼을 누르면 에러가 발생해 웹소켓이 자동으로 연결 해제
- javascript 부분에 내가 close를 잘못 호출했나?
- API url을 요청했을 때 응답하는 코드가 200이 아닌가?
- 핸들러의 sendMessage 부분에 넘어오는 값이 없나?
1. javascript에 잘못된 메소드 호출 문제 ❌
동기 처리 시(페이지가 이동될 때)에 close 메소드가 호출되는 코드말고는 close 메소드를 호출하지 않음
혹시나 해서 close 메소드 호출 부분 위에 실행 전 콘솔에 찍히도록 했는데, 콘솔에 찍힌 것이 없었음!
=> 자바스크립트 부분에서는 문제가 없었다!
2. API url 호출 시 응답하는 코드 문제 ❌
API의 시크릿키가 문제인가 싶어 재발급 받고 변수 선언, 이 부분은 문제가 아니었음
API url을 연결했을때 응답받는 코드를 출력해보니 200 정상 호출되었음
=> API 게이트웨이의 url과 시크릿 키에는 문제가 없었고, 호출 시 응답하는 코드도 문제 없었다!
3. 핸들러의 send 메소드 호출 시 넘어오는 값이 없나? ⭕️ -> 핸들러에서 잘못된 임포트 수정
내가 만든 클래스를 사용해야 정해진 값이 넘어올 수 있는데, 다른 라이브러리 안에 클래스를 임포트하여
값이 null로 리턴되고, 그 null값이 그대로 응답한 리퀘스트에도 전달되어
보낼 값이 없어 웹소켓이 연결을 끊어버린 것
- 해결 방법: 전달되는 값을 받는 VO에 잘못된 임포트를 삭제
임포트 부분도 잘 확인하자
- 잘못된 클래스를 임포트한다면 내가 원한 값과 다른 값이 넘어오니 임포트할때 패키지 확인 잘해야 함 코드의 실행 순서를 명확히 알고 있는 것은 중요하다
- 이번 트러블 슈팅도 코드의 실행 순서를 알고 있어서 빠르게 파악할 수 있었던 문제
- 코드를 사용할때 무작정 붙여넣고, 공부하지 않는다면 어디서 에러가 발생하는지 찾느라 오랜 시간을 허비함
- 그렇기 때문에 코드의 흐름을 명확히 파악하고 사용하는 것이 중요

