노노그래머스 1차
한국소프트웨어산업협회 주관 [회원사 채용연계형 MSA기반 Full Stack 개발 전문가 양성과정 3차]
1차 프로젝트 (9/27~10/24)
NONOGrammers¶
- 팀명 : 도트리키재기
- 팀원 : 유승희(팀장), 전승현, 송기영, 이성수
서비스 배경
개발자에게서 떼어놓을 수 없는 코딩테스트
점차 관성적으로 변하는 코딩테스트 문제 풀이에 부담을 느끼고, 힘들어하는 개발자들에게 힘이 되어 줄 수 있는 서비스를 만들어보자!
서비스 소개
숫자들을 계산하여 칸을 색칠하며 그림을 완성시키는 퍼즐인 '노노그램'에서 힌트를 얻어
코딩테스트 문제를 풀어나갈수록 해당 도트칸들이 색칠되면서 그림을 완성시키는 서비스인 노노그래머스 를 기획했습니다
많은 개발자분들이 '아 하루에 한 문제는 풀어야지'같은 강박에서 벗어나 스스로 문제 풀이에 활력을 느낄 수 있도록 하는 것을 목표로 잡고 시작했습니다
협업 룰
- Slack, Notion 등을 이용한 빠르고 간편한 소통
- Figma, erdcloud 등을 이용한 장소에 구애받지 않는 실시간 아이디어 논의
- JIRA의 스프린트 방식을 이용한 프로젝트 목표 달성
- Git-Flow, Commit-Convention을 활용한 효율적인 협업
- Github의 Pull Request 활용!
Technical Skills
Backend
+ (DB는 AWS LightSail에 배포하여 사용)
Frontend
DB 1차 ERD
- DB ERD
- 위 ERD로는 NONO 기능 구현에 여러 문제가 있다 싶어, 다시 의견을 나누었습니다
-> 노노와 사용자 분리
- 역할 분담
- 주담당/부담당 기능을 맡음으로써, 혹시라도 있을 역할의 아쉬움을 해소하는 동시에 자연스럽게 코드를 공유하고, 서로 도와줄 수 있는 분위기를 조성했습니다
1차 프로젝트 종료 (10/24)¶
- 개발 일정
- 처음에는 교육 수업과 병행으로 진행되어 조금씩 차근차근 수행하다, 13~23일 수업 없는 프로젝트 기간에 집중해서 개발했습니다.
Repository 구조
project-root/
│
├── src/
│ ├── main/
│ │ ├── java/com/dottree/nonogrammers/
│ │ │ ├── controller/
│ │ │ │ ├── MainController.java
│ │ │ │ ├── MyPageController.java
│ │ │ │ ├── PostController.java
│ │ │ │ ├── UrlController.java
│ │ │ │ └── UserController.java
│ │ │ ├── dao/
│ │ │ │ ├── MainMapper.java
│ │ │ │ ├── PostMapper.java
│ │ │ │ └── UserMapper.java
│ │ │ ├── domain/
│ │ │ │ ├── UserDTO.java
│ │ │ │ ├── LikeDTO.java
│ │ │ │ ├── UserNonoDTO.java
│ │ │ │ ├── ..... ..... ...
│ │ │ │ └── PostDTO.java
│ │ │ ├── MyConfig.java
│ │ │ └── NonogrammersApplication.java
│ │ ├── resources/
│ │ │ ├── static/
│ │ │ │ ├── css/
│ │ │ │ │ ├── ...
│ │ │ │ │ └── input.css /* tailwindCSS */
│ │ │ │ ├── js/
│ │ │ │ │ ├── ...
│ │ │ │ │ └── join.js
│ │ │ │ ├── images/
│ │ │ │ └── fonts/
│ │ │ ├── templates/
│ │ │ │ ├── ...
│ │ │ │ └── home.html
│ │ └── banner.txt
│ └── test/java/com/dottree/nonogrammers/NonogrammersApplicationTests.java
├── gradle/wrapper/
├── README.md
├── tailwind.config.js /* tailwindCSS config file */
├── cssrun.sh /* tailwindCSS 실행문 */
├── ...
└── .gitignore
- 변화된 DB ERD
- 개발 과정에서 테이블, 컬럼이 새로 추가되었습니다
<------- 구현 기능 ------->
로그인/로그아웃¶
- Login한 유저의 Session 생성 및 로그아웃 시 제거
GET /login
: Login viewPOST /api/login
: LoginGET /api/logout
: Logout
@PostMapping("/login")
public String login(
JoinDTO dto,
HttpServletRequest httpServletRequest,
Model model
){
Integer userId = dao.getLogin(dto.getEmail(), dto.getPassword());
if (userId != null){ // 로그인 성공
UserDTO userInform = dao.selectUserByUserId(userId);
Map<String, Object> sessionValue = new HashMap<>();
sessionValue.put("userId", userInform.getUserId());
sessionValue.put("email", userInform.getEmail());
sessionValue.put("nickName", userInform.getNickName());
sessionValue.put("profileImgUrl", userInform.getProfileImgUrl());
sessionValue.put("baekjoonUserId", userInform.getBaekjoonUserId());
HttpSession session = httpServletRequest.getSession(false); // 이미 있는 세션을 가져옴
if (session != null) {
session.invalidate(); // 이미 있는 세션을 무효화
}
session = httpServletRequest.getSession(true); // 새로운 세션 생성
session.setAttribute("value", sessionValue);
return "redirect:/";
} else{ // 로그인 실패 (존재하지 않는 유저)
HttpSession session = httpServletRequest.getSession(false);
if (session != null) {
session.invalidate();
}
model.addAttribute("message", "이메일 주소 혹은 비밀번호가 맞지 않습니다");
return "login";
}
}
삽질 노트
로그인 실패 시, 세션을 제거하는 이유는 세션 고정 공격을 방지하기 위함
다만 노노그래머스는 비로그인일 때의 사용자가 가지는 개인정보도 없고, 활동 범위도 굉장히 좁은데.. 그 필요성이 의문이다
해서 나쁠 건 없다지만.. 확실한 근거를 위해서 공부가 필요할 듯
- 세션 고정 공격의 메커니즘:
- 공격자가 세션 생성 : 공격자가 웹사이트에 접속하여 세션ID 발급
- 세션 ID 전달 : 피해자에게 위에서 발급받은 세션ID 전달
- 피해자는 공격자가 제공한 세션ID를 통해 활동하고, 공격자는 이를 탈취할 수 있음
회원가입¶
- 유저 데이터 DB에 생성
GET /join
: Join viewPOST /api/join
: JoinPOST api/check/{checkValue}
: 이메일, 닉네임 중복 체크
비밀번호 변경¶
- 총 3개의 End-point를 통해 비밀번호 변경 과정 진행
GET /forgot-password
: Forgot-password viewPOST /api/reset-password-token
: 전달받은 email 검증하고, 토큰을 생성하여 해당 토큰이 달린 url("api/check-reset-password?token="+token)을 Response로 전달GET /api/check-reset-password
: 전달받은 token을 검증하고, 검증된 token이면 Reset-password view로 전달POST /api/reset-password
: 비밀번호 변경
글 작성¶
POST /post/write
: 다중 이미지 파일 업로드 및 글 작성 (이미지 파일은 uuid로 저장)
삽질 노트
octet-stream
처음에는, 사용자가 파일을 올리지 않으면 null 값이 들어오는 줄 알고 다음과 같이 처리했었다
MultipartFile[] uploadImageFiles = vo.getUploadImageFiles(); // UploadImageVO vo
if (vo.getUploadImageFiles() != null){...}
파일 유무를 체크하려면
isEmpty()
로!
MultipartFile[] uploadImageFiles = vo.getUploadImageFiles();
if (!uploadImageFiles[0].isEmpty()){...}
mybatis PK id 가져오기
Post, File 테이블이 나뉘어 있기 때문에, 새로 생성된 Post의 ID를 얻어 File 테이블에 넣어주어야 했다.
@Options 코드를 통해 insert함으로써 생성되는 postId를 insert와 동시에 바로 얻어 사용할 수 있었당글 좋아요¶
POST /post/like
: 게시글 좋아요
flowchart LR
verify("해당 유저가 해당 게시글을 좋아요 한 이력이 있는가?")
verify --No--> post["좋아요 데이터 생성"]
verify --Yes--> delete["좋아요 데이터 삭제"]
result("해당 게시글의 좋아요 수 반환")
post ----> result
delete ----> result
Home, Terms¶
- Home view, Terms view 구현