Skip to content

hmm-umm/contap

 
 

Repository files navigation

Contap BackEnd

팀원의 장점

한우석

  • 이승준
    • 어떤 부탁을 드려도 정말 척척 해결 해주셨습니다. 덕분에 부담없이 필요한 기능들을 요청할 수 있어서 너무 좋았습니다. 서버 처리 속도도 되게 꼼꼼하게 신경 써주셔서 저희 사이트가 훨씬 더 쾌적해 졌다고 생각합니다. 매우 뛰어난 실력을 가지셨음에도 겸손하시고 항상 꼼꼼하게 코드 리뷰도 해주시면서 백엔드의 기둥같은 역할을 잘 해주셔서 너무 감사합니다.
  • 오준석
    • 미니때도 같이 프로젝트를 진행 했었는데 그때 보다 훨씬 많은 성장을 하셨다고 생각합니다. 저와 같이 항상 마지막 까지 게더에 남아서 다른 짜잘한 기능들을 같이 진행 했었는데 그렇게 필요하지 않은 기능인데도 불구하고 막히면 끝까지 해결 하려고 하시는 모습이 인상적이였습니다. 프로젝트 내,외적으로 문제가 생기면 가장 먼저 뛰어가고 팀을 위해주셔서 우리팀이 웃으면서 여기까지 올 수 있었습니다. 그리고 솔직히 소셜로그인, HTTPS모두 준석님 아니였으면 못했을 것 같아요!
  • 김혜림
    • 항상 밝은모습으로 맡은 일은 끝까지 해내고 마는 모습이 좋았습니다. 똑 부러지게 설명도 잘 해주시고 힘든 내색도 잘 하지 않으셨어요. 매번 회의를 진행 할 때도 정말 열심히 참여해주셔서 이렇게 좋은 결과물이 나왔다고 생각합니다. 저희 왕언니에요!
  • 김동우
    • 담당하신 부분에 대해서 어떻게든 찾아 문제를 해결 하시는 모습이 너무 든든 했습니다. 그래서 더 제 파트에 집중할 수 있었던 것 같아요! 의지가 많이 되었습니다. 좋은 아이디어를 많이 내주셔서 감사합니다.
  • 이아영
    • 거의 마이페이지에 감금당해 있으시던 우리 아영님.. 그래도 결국 이기셔서 다행입니다 ㅜㅜ 정말 끝까지 포기하지 않고 늦은 시간까지 너무 고생 많으셨어요! 저한테 물어보실때 계속 너무 죄송해 하셨는데 그러지 않아도 괜찮아요 덕분에 저도 많이 배울 수 있어서 너무 의미있었던 시간이였습니다. 덕분에 마이페이지 진짜 이쁘게 나왔어요 너무 감사하고 고생 많으셨습니다~
  • 김민지
    • 이번 실전프로젝트 모든 디자이너를 통틀어서 가장 열심히 참여 해주셨다고 확신합니다. UX/UI, 라이팅 등 뭐 하나 이유없이 작업하시지 않고 꼼꼼하게 신경 쓰시는 모습에 너무 많은 것을 배웠습니다. 제 의견을 정말 잘 들어주시고 피드백도 꼼꼼하게 해주셔서 저희 사이트 디자인이 그렇게 칭찬을 받았다고 생각해요! 하루도 빠지지 않고 게더에 계속 들어와 계시고 혼자여서 많이 부담 되었을텐데도 불구하고 정말 디자인을 잘 뽑아 주셨습니다. 전 저희 사이트 디자인이 세상에서 제일 이뻐요 채구채구

오준석

  • 이승준
    • 코드 이해력이 너무 좋으셨습니다. 남이 짠 코드도 쉽게 이해한 뒤에 알려주셨고, 전반적으로 코드를 어떻게 생각하며 짜면 좋을지 생각하는 방법을 많이 배웠습니다.어떤 질문이던 마다하지 않고 친절하게 알려주셨습니다.언제나 밝은 모습으로 팀원들을 항상 반겨주는 모습에 많은것을 배운 시간이었습니다.많은걸 알려주셔서 너무 감사합니다.
  • 김혜림
    • 항상 긍정적이시고 어떤 도움도 거절하지 않고 항상 밝은모습으로 도와주셨습니다. 워낙 경력도 있으신 분이었어서 코드를 작성하는데 있어서도 배울점이 많았습니다.그리고 좋은 분위기를 형성해주시려고 항상 노력해주셨고 어떤말이던 귀담아 들어주셔서 너무너무 감사했습니다. 많은걸 배울수 있게 해줘서 감사합니다.
  • 김동우
    • 팀장으로서 팀원들의 의견을 귀 귀울이려고 노력하시는 모습이 멋졌습니다.팀원의 생각이라면 절대 놓치지 않으려고 하시는 모습이 너무 인상깊었습니다. 팀장일하는게 만만치 않은 일 일텐데 개의치않아하시고 항상 팀원을 먼저 생각하려 하시는 모습에서 많은것을 배웠습니다.팀원들을 잘 이끌어주셔서 감사합니다.
  • 한우석
    • 미니프로젝트때도 한팀이었기에 너무나도 서로를 잘 아는 상태였습니다. 역시나 이번 프로젝트때도 서로의 케미가 빛을 내주었고,항상 밝은 모습과 책임감 있는 모습은 여전히 최고였습니다. 새벽까지 저와함께 짜잘한 버그들을 같이 수정하고 안되는것도 같이 헤딩하면서 많이 성장하는 시간이 되었던것 같습니다.함께 늦게까지 노력해줘서 감사합니다.
  • 이아영
    • 언제나 긍정적이시고 막히시면 언제나 질문 하시며 더 실력을 늘리고 싶어하는 열정이 너무나도 많이 보였었습니다. 본인 맡은 바를 어떻게해서든 끝내려는 모습도 인상깊었습니다. 본인이 항상 부족하다고 생각하시면서 겸손한 자세로 공부하는 모습을 볼때마다 나도 저렇게 해야겠다 라는 생각을 정말 많이 했던것 같습니다. 언제나 밝은모습 보여주셔서 감사합니다.
  • 김민지
    • 개발자가 아닌 디자이너이시기에 제가 감히 실력을 논할순 없지만 제가 느끼기엔 실력적으로도 너무 출중하셨고, 어떤 디자인의 요구가 들어오더라도 마다하지않고 다 반영하려고 노력해주셨던 모습이 본받아야 겠다라고 생각했습니다.학교를 다니시는 와중에도 디자이너일 외적으로도 팀원들과 자주 소통하고 정말 팀의 일부가 되어 함께하는 모습이 너무 인상 깊었습니다.언제나 긍정적으로 임해주셔서 감사합니다.

김민지

  • 이승준
    • 백엔드는 잘 모르지만 서버 최적화에 많은 힘을 써주신 것 같습니다. 덕분에 안정성 있는 홈페이지를 만들 수 있었던 것 같습니다. 또한 같은 팀 외에도 다른 팀 백엔드 개발자도 성심성의껏 도와주시는 점이 인상적이었습니다. 무뚝뚝한 말투에 그렇지 못한 따뜻한 심성에 같이 일하면서 재밌고 즐거웠습니다. 감사했습니다!
  • 오준석
    • 팀의 문제 해결사이자 분위기 메이커를 담당하셨습니다. 덕분에 트러블 없이 즐겁게 프로젝트를 진행할 수 있었고 의지할 수 있었습니다. 적극적으로 유저 테스트를 진행하거나, 팀원들의 개발 일정 체크하는 등 팀의 전반적인 부분에 기여하셨기 때문에 백엔드 개발자 외에도 PM과 같은 역량을 볼 수 있었습니다. 준석님이 있어서 모두가 즐거웠습니다. 감사합니다.
  • 김혜림
    • 백엔드 개발자지만 나은 서비스를 위해 기획면에서도 고민할 줄 아셨습니다. 기획 중에 흩어졌던 대화들을 정리하고 다음 방향을 제시하는데 탁월하셨습니다. 개발에서도 문제가 있으면 꼭 해결하려는 끈기와, 그 누구보다 성실하신 혜림님을 보며 같이 열심히 해야겠다고 생각했습니다. 고생 많으셨습니다.
  • 김동우
    • 팀장으로서 팀을 이끌어가는데 부담이 크셨을 거라 생각합니다. 하지만 그런 내색 없이 항상 팀원들의 이야기에 귀 기울여주셨고 책임감 있게 팀을 이끌어 주셨습니다. 또한 중요한 디자인이 있으면 어렵더라도 반영해 주시려고 노력해 주신 점이 감사했습니다.
  • 한우석
    • '좋은 프런트엔드 개발자'의 자질을 모두 갖추신 것 같습니다. 실력, 디자인 안목, 꼼꼼함, UX 이해도, 좋은 결과물을 위해 디자인을 최대한 구현하려는 노력까지 배우고 싶은 점이 많았습니다. 특히나 디자이너와의 커뮤니케이션 능력이 협업에서 빛을 발해 즐겁게 일할 수 있었습니다. 기억에 남는 개발자가 될 것 같습니다.
  • 이아영
    • 개발 중에 담당 파트에서 디자인이 많이 수정되었지만 UI의 개선을 위해 잔수정도 마다하지 않으셨습니다. 항상 따뜻하고 웃는 얼굴로 팀원들을 대해주셨고 덕분에 팀 분위기는 물론 피드백의 과정마저 즐거웠습니다. 또한 개발 중간중간 디자이너와 커뮤니케이션을 위해 노력하시는 점이 인상 깊었습니다. 너무 감사했습니다 아영님.

이승준

  • 오준석
    • 덕분에 6주동안 웃으면서 지낼수 있었습니다. 광대가 아니라 항상 좋은분위기를 만들어주셔서 재밌는 환경에 개발할수있어서 감사하게 생각하고 있습니다. 문서정리도 잘해주시고 , sentry,https,이메일 인증 등 새로운 기술을 적용해주셔서 서비스의 완성도를 높일수 있었던것 같습니다.
  • 김혜림
    • 어려운 문제도 끈기를 갖고 해결하려는 모습이 보기좋았습니다. 개발 과정중에서 문제가 발생했을때 혼자 공부하시면서 처리해주시고, 그내용까지 상세하게 정리해서 설명해주셔서 감사하게 생각하고있습니다.그리고 여러가지 자잘한 부분도 섬세하게 신경써주셔서 놓치고 갈수있던 부분들도 많이 잡아주셨습니다.!
  • 김동우
    • 팀장역할을 잘 해주셔서 감사하게 생각하고있습니다. 항해 1주차때 같은조로 만났었는데 열심히 하는 모습이 보기좋았었는데 그때 그모습 그대로 변하지않고 초심 그대로 열심히 하시는 모습을 보면서 저도 동기부여를 많이 받은것 같습니다. 변하지 않고 열심히 하길 바랄게요...!!
  • 한우석
    • 프론트 개발자 분들중에 제일 고생을 많이 하신것 같습니다. 여러가지 기술적인 측면도 해결해주시고 자잘한 부분까지 꼼꼼하게 신경써주셔서 좋은결과물을 얻어낼수 있었던것 같습니다.재밌는 성격 덕분에 6주동안 재밌게 지낼수있어서 좋았어요.. 정말 열심히 하시고 잘하시는 분이라서 성큼성큼 성장할것 같네요!
  • 이아영
    • 항상 긍정적인 분입니다. 어떤 상황에서도 밝게대해 주실것같아서 어떤 얘기던 편하게 얘기할수있을것같다는 생각이 드는 분입니다. 또한 일이 해결될때까지 자리를 지키는 모습도 정말 책임감있고 멋있으신것같습니다. 프로젝트 막바지에는 항상 해뜰때까지 하셧던것 같네요. 덕분에 클라이언트 부분이 더완성도가 높아진것같습니다. 정말 고생하셧어요.
  • 김민지
    • 수업까지들으시고 다른 사이드 프로젝트까지 진행하시면서 디자인을 만들어 주셨습니다.디자인에대한 안목은 없지만 정말 누가봐도 이쁜 디자인을 만들어주셔서 감사하게 생각하고 있어요, 바쁘신 와중에도 항상 게더에 접속해서 밤늦게까지 같이 열심히 일 해 주시고, 의사소통도 적극적으로 해주신것 같아요 다시한번 감사드립니다.

이아영

  • 이승준
    • 프론트에서 필요한 요청이 있는지 항상 신경써주셔서 정말 감사했으며, 프론트 코드도 공부하시면서 어려운 부분 같이 보면서 도와주셔서 감사합니다. 묵묵히 맡으신 부분 책임감있게 마무리해주셔서 멋지십니다!
  • 오준석
    • 개발하면서 힘들어할때 좋은 말씀으로 응원해주셔서 정말 감사했으며, https 연결 성공하시고 다른조원 분들에게도 알려주셨던 모습이 멋지십니다! 팀 프로젝트가 좋은 방향으로 갈수있게 많은 아이디어 말씀해주셔서 덕분에 좋은 결과물을 얻어가게 되어 감사합니다.
  • 김혜림
    • 마이페이지 추가 요청사항나 변경사항이 초반에 자주있었는데 바로바로 수정해주시고, 마이페이지 편하게 작업할 수 있어서 정말 감사했습니다. 매일 알고리즘 공부하시는 열정적인 모습이 멋지십니다!
  • 김동우
    • 팀장님으로서 6주동안 팀을 이끄시느라 고생많으셨습니다! 항상 팀원들 말씀에 귀기울여 주시고 팀장역할과 개발 두가지를 동시에 하시느라 힘들고 부담도 되셨을텐데 잘해주셔서 정말 멋지십니다! 마이페이지 검색기능 맡아서 마무리 해주시고, 자잘한 질문들 같이 봐주셔서 정말 감사합니다.
  • 한우석
    • 프론트엔드 부분 전체적인 피드백 꼼꼼하게 챙겨주셔서 감사합니다. 잠을 포기하시면서 맡은 부분 책임감있게 끝내시고 프론트엔드 해결사 역할 해주셔서 감사했고 정말 멋지십니다! 6주동안 제가 질문 많이 했는데 매번 자세히 설명해주셔서 개발 꿀팁들도 많이 알아가고 배웠습니다ㅠㅠ 덕분에 프로젝트 무사히 끝낼 수 있었습니다. 정말 감사합니다 우석님!
  • 김민지
    • 이번 프로젝트 기획짤때 아이디어가 넘치는 모습이 정말 멋지셨습니다. UX적인 부분을 고려해서 디자인하시고 라이팅하시는 모습을 통해 웹페이지들이 그냥 나오는게 아니구나를 알게되고 덕분에 UX적인 부분을 많이 배워서 유익했습니다. 매번 회의에 참여해주시고, 열심히 작업해주시는 모습에 감동이였으며 감사했습니다. 민지님의 밝음이 그리울거 같습니다. 저희팀과 즐겁게 작업해주시고 멋진 결과물 만들어주셔서 정말 감사합니다 민지님!

김동우

  • 이승준
    • 첫 프로젝트 때 기획이 막판에 바뀌어 쉽지 않았을텐데도 완벽하게 해내는 모습이 멋져 이번에도 같이 프로젝트를 하게 되었습니다. 6주 동안에 긴 여정에도 검색, 무한스크롤 등 저와 함께하는 작업을 너무나 잘 만들어 주셨습니다. 앞으로 더 어렵고 복잡한 기능을 쉽게 만느는 능력자가 되실거라 믿어 의심치 않습니다!
  • 오준석
    • 6주동안의 긴 여정에서 제가 팀장으로서 부족한 부분이 있을 때 같이 분위기를 살려주고 이끌어 주셔서 무사히 잘 마친 것 같습니다. 기능적인 부분에서도 로그인, 회원가입, 탈퇴, 비밀번호 변경 등을 제가 어렵지 않게 작업할 수 있도록 도와주셨습니다. 프로젝트 내외적으로 너무 든든했습니다!
  • 김혜림
    • 저와 같은 아침형 인간이신 혜림님ㅋㅋㅋ 저와는 알람 기능을 작업했는데 처음 들어 잘 이해가 안가는 비트연산자 개념을 친절하게 설명해주셔서 어렵지 않게 기능을 만들 수 있었던 것 같습니다. 그리고 최종 발표자료에 디테일한 부분을 잡아주셔서 좀 더 발표를 편하게 할 수 있었던 것 같습니다! 다음에도 멋진 동료 개발자로서 멋진 프로젝트 같이 하고 싶습니다!
  • 한우석
    • 이번에 가장 중요한 채팅이란 생소할 수 있는 기능을 맡았지만 멋지게 해내주셔서 감사합니다. 덕분에 제 기능에만 집중을 할 수 있었습니다. 그리고 잠을 줄여가며 저희 사이트의 전체적인 디테일을 잡아주셔서 감사합니다. 메인페이지 이펙트를 멋지게 해주신 걸 봤을 때 감동은 잊을 수 없습니다ㅎㅎ 어떤 어려운 기능이라도 해내실 수 있는 개발자가 될 것 같습니다!
  • 이아영
    • 승준님과 마찬가지로 첫 프로젝트 때 스타일링이 어려웠었는데 너무 멋지게 바꿔주셔서 같이 프로젝트를 하게 됐습니다. 이번에 카드 앞면, 마이페이지의 상세하고 디테일한 기능을 위해 여러 번 수정을 하며 작업 한 결과 지금의 멋있는 결과물이 나올 수 있었습니다. 디자인 감각도 뛰어난 멋진 프론트엔드 개발자가 될거라 믿어 의심치 않습니다!
  • 김민지
    • 디자인으로 프로젝트의 시작을 열고, 프로젝트 썸네일로 마무리를 지어주셨습니다. 물론 그 사이에도 수많은 디자인 수정과 마케팅도 맡아서 해주셨습니다. 디자이너분과 첫 협업인데 너무 잘 마무리 되어 처음 해보는 협업에 대한 두려움도 없앨 수 있었던 것 같습니다. 무엇보다 멋진 디자인으로 다른 페이지보다 더욱 고급스러워 보이게 만들어주신 민지님 채구

김혜림

  • 이승준
    • 아이디어가 뛰어나며, 집중력 있게 만들어내었습니다. 개발적으로 많이 배울 수 있었습니다. 늦게까지 많은 일을 맡아 프로젝트 진행에 힘써주었습니다.
  • 오준석
    • 타인에게 배려가 많습니다. 배우고자 하는 마음이 커서, 3개월 전보다 지금 실력이 많이 늘었습니다. 또한 늦게까지 남아 일을 하시며 프로젝트에 많은 부분을 맡아주셨습니다.
  • 김동우
    • 맡은 바에 욕심이 있어 끝까지 책임감 있게 준비하는 모습을 볼 수 있었습니다. 또한 팀장으로서 팀의 분위기를 잡는 것에 최선을 다 하신 것 같습니다.
  • 한우석
    • 완성도 높은 프론트엔드 개발을 위해 노력하는 모습을 볼 수 있었습니다. 책임감 있게 프로젝트 진행에 도움 주셨습니다.
  • 이아영
    • 팀이 원활하게 돌아가도록 긍정적으로 임해주셨습니다. 언제나 질문하시며 더 실력을 키우시려는 모습과 기능 개발에 최선을 다하시는 모습이 멋졌습니다.
  • 김민지
    • 전체 팀 중 가장 열심히 하시는 디자이너로 뽑을 수 있었을 것 같습니다. 대학생임에도 불구하고 매일 게더에 오래 계시고 회의에 꼭 참여하시는, 디자인에 적극 참여하시는 모습이 좋았습니다.
Backend Rules

Structure

  1. 패키지는 목적별로 묶는다.
  • user(User 관련 패키지), card(카드 관련 패키지)
  1. Controller에서는 최대한 어떤 Service를 호출할지 결정하는 역할과 Exception처리만을 담당하자.
  • Controller 단에서 로직을 구현하는 것을 지양한다.
  • Controller의 코드 라인 수를 줄이자는 뜻은 절대 아니다.
  1. 하나의 메소드와 클래스는 하나의 목적을 두게 만든다.
  • 하나의 메소드 안에서 한가지 일만 해야한다.
  • 하나의 클래스 안에서는 같은 목적을 둔 코드들의 집합이여야한다.
  1. 메소드와 클래스는 최대한 작게 만든다.
  • 메소드와 클래스가 커진다면 하나의 클래스나 메소드 안에서 여러 동작을 하고 있을 확률이 크다.
  • 수많은 책임을 떠안은 클래스를 피한다. 큰 클래스 몇 개가 아니라 작은 클래스 여럿으로 이뤄진 시스템이 더욱 바람직하다.
  • 클래스 나누는 것을 두려워하지 말자.
  1. 도메인 서비스를 만들어지는 것을 피하자.
  • User라는 도메인이 있을 때, UserService로 만드는 것을 피한다.
  • 이렇게 도메인 네이밍을 딴 서비스가 만들어지면 자연스레 수많은 책임을 떠안은 큰 클래스로 발전될 가능성이 높다.
  • 기능 별로 세분화해서 만들어보자. (UserService, EmailService 등...)

Programming

  1. 반복되는 코드를 작성하지 않는다.
  • 단, 테스트코드는 예외로 한다.
  1. 변수는 최대한 사용하는 위치에 가깝게 사용한다.
  2. 파라미터 변수와 내부 변수를 구별할 땐 언더바가 아닌 this로 구별한다.
  • this.name = name (O) / name = _name (X)
  • 추가적으로 언더바를 prefix로 사용하는 것을 지양하자.
  1. 코드의 길이가 짧고 명료한 것도 좋지만, 가독성이 현저히 떨어진다면 코드를 좀 더 풀어쓴다.
  • 무조건적으로 코드가 짧은 것이 좋다고 생각하지 않는다. 다른 개발자가 본다면 가독성이 현저히 떨어진다.
  1. 모든 예외는 무시하지말고 처리한다. 만약 예외를 처리하지 않을거라면 그 이유에 대해서 명확하게 주석을 남긴다.
  2. 예외를 던질 때는 최대한 세부적인 Exception(= Custom Exception)을 던진다.
  • 실패한 코드의 의도를 파악하려면 호출 스택트레이스만으로 부족하다.
  • 오류 메세지에 전후 상황의 정보를 담아 예외와 함께 던진다.
  1. 예외 케이스가 발생할 확률이 있는 경우, 가능한 빨리 리턴 또는 예외를 던지도록 작성한다.
  • 쓸데없이 정상로직을 태울 필요가 없게한다.
  1. 조건이 복잡한 경우 임시 boolean 변수를 만들어 단순화한다.
  2. 최대한 객체 타입 대신 기본 자료형을 선택하고, 생각지도 못한 Autoboxing이 발생하지 않도록 유의한다.

Name

  1. 변수는 CamelCase를 기본으로 하자!
  • userEmail,userName,pwCheck 등등
  1. 패키지명은 단어가 달라지더라도 무조건 소문자를 사용하자!
  2. ENUM이나 상수는 대문자로 네이밍한다.
  • REJECT_TAP,ACCEPT_TAP 등등
  1. 함수명은 소문자로 시작하고 동사로 네이밍한다.
  • getUserId(), getEmail() 등등
  1. 클래스명은 명사로 작성하고 UpperCamelCase를 사용한다.
  • ChatController , HashTag 등등
  1. 객체 이름을 함수 이름에 중복해서 넣지 않는다. (= 상위 이름을 하위 이름에 중복시키지 않는다.)
  • user.getAuthStatus()
  1. 의도가 드러난다면 되도록 짧은 이름을 선택한다.
  • 단, 축약형을 선택하는 경우는 개발자의 의도가 명백히 전달되는 경우이다. 명백히 전달이 안된다면 축약형보다 서술형이 더 좋다.
  1. 함수의 부수효과를 설명한다.
  • 함수는 한가지 동작만 수행하는 것이 좋지만, 때에 따라 부수 효과를 일으킬 수도 있다.
  1. LocalDateTime -> xxxAt, LocalDate -> xxxDt로 네이밍

BackEnd(Language,Library,Framework)

  • Java8
  • JDK 1.8.0
  • Spring Boot
  • IntelliJ IDEA
  • Spring Data JPA
  • Swagger
  • JWT
  • Spring Security
  • Sentry
  • Websocket , SockJS , Stomp
  • Redis
  • QueryDSL
  • MYSQL

Project Introduce

디자이너와 개발자를 위한 커뮤니티 플랫폼 'Contap'

프로젝트로 나를 소개하고

함께 일하고 싶은 디자이너와 개발자를 만날 수 있는 곳!

프로젝트를 한곳에 모아 아카이빙 할 수 있어요.

Project Intention

백엔드와 프론트엔드의 프로젝트나 협업은 생각보다 쉽지 않습니다. 디자이너와의 협업은 더더욱 쉽지 않구요.

그러기 힘든 이유로는 이제 막 개발을 배우기 시작한 주니어 개발자, 디자이너들은 아직 협업 경험도 별로 없기도 하고 서로의 정보들이 한 곳에 모여있지 않아서, 그 정보를 보고 이야기를 할 수 있는 공간 또한 많지 않기 때문 이라고 생각했습니다.

그래서 저희는 이 문제를 해결하고자 개발자와 디자이너가 서로의 프로젝트를 공유하고 더 나아가서는 프로젝트를 진행 할 사람을 만날 수 있는 사이트인 Contap을 만들게 되었습니다.

Target

  • Developer (FrontEnd/BackEnd)
  • Designer

Team Introduce

  • FrontEnd React : 김동우,이아영,한우석
  • BackEnd Spring Boot : 이승준,오준석,김혜림
  • Designer UX/UI : 김민지

Service Introduce

  • 일반회원가입은 이메일 인증을 통해 가입할 수 있으며, 그 외에 카카오톡,깃허브로 로그인할 수 있습니다.
  • 메인페이지에서 작성된 카드들을 확인할 수 있습니다.
  • 마이페이지에서 카드의 앞면에 수정버튼을 누를 시 나오는 해쉬태그 목록에서 사용하는 기술 및 관심있는 항목을 선택할 수 있습니다.
  • 카드의 뒷면에는 내가 진행했던 프로젝트를 설명하는 내용을 작성할 수 있습니다.
  • 메인페이지에서 카드들을 확인한 뒤 마음에 드는 사람에게 간단한 쪽지내용과 함께 Tap요청을 보낼 수 있습니다.
  • Tap요청을 받은 사람은 보낸 사람의 카드내용을 확인한 뒤에 수락 및 거절을 할 수 있습니다.
  • Tap요청을 수락하면 나의 Grab항목에 추가되며 Grab된 사람과 실시간으로 1:1채팅을 할 수 있습니다.
  • 채팅이 종료되거나 채팅시 불쾌한 내용이 오갈 시 그랩을 끊을 수 있습니다.

ERD(Entity-Relationship Diagram)

ERD Image

Trouble Shooting

카드조회
처음엔 앞면만 보여주는 페이지에서도 뒷면정보(상세내용)까지 DB에서 불러오는 방법을 선택했었습니다. 이렇게 선택하게 된 이유는 DB에 접촉을 줄이고, 프론트에서도 서버에 접촉을 적게 하고 싶다해서 테스트를 진행해 보았는데 결과적으로 테스트의 결과가 성능이 좋게 나왔습니다. 나중에 알게되었지만 테스트 자체의 방법도 잘모 되었다는걸 알게되었습니다. 이 이유는 검색 쿼리에서 속도가 다시 느리게 나왔기 때문입니다.
  • 랜덤한 유저 9명을 뽑아오는 테스트 속도 - 10ms
  • 검색하였을때 테스트속도 - 600ms
  • 위와 같은 현상으로 앞면만 보여주는 페이지에서는 뒷면정보를 불러오지 않게끔 수정 하였습니다.
  • 랜덤한 유저 9명을 뽑아오는 테스트 속도 - 1~5ms
  • 검색하였을때 테스트속도 - 100ms 이상은 거의 나오지 않았습니다.
Cascade
  • 회원탈퇴 진행시 뒷면카드(상세내용)이 존재하면 탈퇴가 되지 않는 문제가 생겼습니다.
  • 아무래도 유저와 연관관계가 너무 많이 걸려있어서 일거라 생각이 들었습니다.
  • 처음엔 User Entity에 있는 @OneToMany List 에 CascadeType.All을 적용하였지만, 뒷면카드(상세내용)이 존재하더라도 탈퇴는 원활하게 되었는데,뒷면카드(상세내용)만 삭제하려고 했을때 삭제가 되지않는 오류가 발생했습니다.
  • 다음 방법으론 CascadeType.REMOVE를 적용하였는데 두 문제다 해결이 되었지만,ALL이 REMOVE보다 더 상위 개념으로 인지하고 있었는데 왜 ALL은 되지 않았을까? 그래서 당장의 해결에 집중하는것보다는 위의 궁금증을 해결하기위해서 구글링한 결과 CascadeType.ALL을 orphanRemoval = true 와 함께쓰면 된다는 블로그를 보게되어 적용하였는데 역시나 되지않습니다. 하지만 또 이상하게도 CascadeType을 생략하고 orpahRemoval = true만 적용했을때는 탈퇴,뒷면카드개별삭제 둘다 가능했다. 이 부분 현재 기능자체는 해결이 되었지만 우리가 Cascade를 아직 정확하게 알지 못하여서 궁금증에 대한 해결은 현재 미해결 상태이므로 추후에 더 공부를 한 뒤에 해결방법을 찾을 예정입니다.
회원탈퇴 방식 변경
  • 처음에는 회원탈퇴를 누르는 즉시 사용자 테이블에서 사용자 정보가 모두 삭제 되게끔 Cascade 적용하여 진행하려 했습니다.
  • 하지만 현재 저희 서비스구조상 사용자와 연관관계가 많이 형성이 되어 있었기 때문에 관련된 모든 부분에 Cascade를 적용해야지만 탈퇴가 가능하게 처리가 됐었습니다.
  • 이렇게 했을때 탈퇴 자체는 어려운 부분은 아니었습니다.
  • 하지만 이 과정속에서 회원탈퇴 처리를 현업에는 어떻게 진행하는지 의문이 생겼습니다.
  • 그래서 멘토님들한테도 여쭤보고,여러 사이트들을 참고해보니 회원탈퇴가 즉시 실시간 데이터삭제가 아닌 탈퇴를 하더라도 일정기간 사용자의 정보를 가지고 있다는것을 알게되었습니다.
  • 그리고 사용자의 정보들은 의존성이 강함을 캐치하였고 사용자 정보 삭제 시 Cascade대신 하위 데이터부터 삭제하는게 적절하다고 생각을 했습니다.
  • 그래서 저희는 회원탈퇴를 스프링 스케줄러를 이용해 사용자의 status를 관리하며 탈퇴를 하더라도 한 달 간 휴면 계정으로 관리되며, 한 달 후 사용자의 정보가 삭제되게끔 로직을 변경하였습니다.
검색기능
  • 저희 서비스의 User와 HasTag의 테이블구조는 보이는 이미지와 같은 형태로 구성되어 있는데요.
  • HashTag로 검색을하면 선택한 HashTag를 토대로 User가 검색결과로 도출 되게끔 구현하려 했습니다.
  • 여기서 User 테이블과 HashTag테이블이 다대다 관계를 갖고있기에 중간테이블이 존재했는데,기존에는 JPQL을 사용하고 있어 and검색을 하기엔 쿼리문이 너무 복잡해져 OR검색으로 구현하였습니다.
  • 여기서 and검색을 구현하기 위해선 어떻게 해야할지 고민 하던중 User와 HashTag의 관계를 중간테이블에서 관리하는것이 아닌 User테이블에서 HashTag에 관련된 데이터를 관리하면 어떨까 라는 생각을 했었는데 이러한 방식이 반정규화라는 것임을 알게되었습니다.
  • User테이블에 HashTagString이라는 String 자료형 컬럼을 추가하고 축구와 Java를 좋아하는 유저라면 @Java@_@축구@ 와 같은 형태로 저장하였습니다.
  • 이렇게 함으로서 이전에 포기했던 and검색을 구현할 수 있게 되었고,성능적인면에서도 테스트를 진행 하였는데 5000명의 User가 랜덤한 HasTag 4개를 갖도록 설정해준 뒤에 중간테이블을 사용한 검색과 반정규화한 테이블을 사용한 검색을 비교하였을때 전자는 11.6ms가 나왔고 후자는 7.63ms가 나왔기 때문에 최종적으로는 반정규화한 테이블을 사용한 검색을 적용하였습니다.
하나의 Redis 서버가 Shutdown 된다면?
  • pub/sub이 중요하기 때문에 레디스를 죽지않도록 대비가 필요하다고 생각했습니다.
  • 처음에는 서버 장애 발생시 기본적으로 불필요한 key를 삭제하거나 서버를 새로 추가한다던가 데이터 설계를 변경해 보관장소를 Redis에서 RDB로 변경하는 방법들이 있다고 파악하고 있는데 현재 구상중인 방법으로는 채팅내용같은 중요한 데이터는 RDB에 기록하고 캐시만 Redis에 저장하고 사라져도 좋은 데이터라면 Redis에 저장을 시켜보려 하였습니다.
  • 하지만 이 부분은 이론적인 부분만 찾아서 서비스에 접목시키기에는 이해도가 너무 부족했었습니다.
  • 서버 다운이 됐을때 대처 방안을 여러 방법들을 찾아본 결과로 가장 참고자료가 많았었던 Redis Sentinel 로 진행했습니다.
  • Sentinel은 HA 무중단서비스를 지원하고 마스터와 슬레이브구조에 센티넬을 추가해 각각의 서버들을 감시하도록 하는 구조로 되어있으며 마스터를 감시하다가 다운되면 슬레이브를 마스터로 승격시키고 다운되었던 마스터가 재기동되면 센티넬이 해당 마스터를 슬레이브로 전환시키는 구조였습니다.
SSL인증 발급 이슈 (미해결)
$ sudo certbot --nginx -d contap.shop -d www.contap.shop
  • 이전에 Nginx Configuration 도 진행하였고 인증서를 발급받는 위의 명령어를 실행한 이후에 발생하였습니다.
Domain: contap.shop
Type: connection
Detail: Fetching
http:https://contap.shop/.well-known/acme-challenge/eI2sMNZH0hZ-XJwpw625SzdbauGMG5cex5uvVO2hWaI: 1
Timeout during connect (likely firewall problem)

Domain: www.contap.shop
Type: connection
Detail: Fetching
http:https://www.contap.shop/.well-known/acme-challenge/eI2sMNZH0hZ-XJwpw625SzdbauGMG5cex5uvVO2hWaI: 1
Timeout during connect (likely firewall problem)

To fix these errors, please make sure that your domain name was
entered correctly and the DNS A/AAAA record(s) for that domain
contain(s) the right IP address. Additionally, please check that
your computer has a publicly routable IP address and that no
firewalls are preventing the server from communicating with the
client. If you’re using the webroot plugin, you should also verify
that you are serving files from the webroot path you provided.

  • 우분투에서 cerbot으로 인증서를 발급받는 과정에서 위와 같은 오류가 발생했습니다.
  • 이게 처음에는 사실 한번에 인증서를 발급 받았었습니다. 그런데 좀더 공부도하고싶고 다시 해보면서 하려고 기존에 인증서가 깔려있던 ec2를 지운뒤에 다시 재발급하는 과정에서 이슈가 나왔습니다.
  • 구글에 위와같은 오류를 검색해보니 80포트를 열어보라해서 ec2에서도 확인하고 우분투 내에서도 80포트를 일부러도 끊었다가 다시키기도해보고 가비아에서 dns설정에 ip값이 제대로 들어가있나 확인도 해보고 도메인도 5개정도 새로 발급받음과 동시에 ec2도 계속 새로 생성(약20개정도 새로생성해봤음..)하면서 진행해보았지만 해결이 전혀 되지 않았습니다.
  • 그외에 구글링으로 저 오류를 검색해 약 30페이지에 다르는 이휴 해결 내용들을 확인하며 제시해준 해결방법들을 진행해보았지만 역시나 되지 않았습니다.
  • 그래서 든 생각이 혹시 내가 너무많은 요청을 해서 막힌건가 라는 생각이 들기도 하였습니다.
  • 그래서 아이피도 바꿔서 진행해봤지만...결론은 실패했습니다..
  • 여러방법들을 약 2일에 걸쳐서 시도해보았지만 계속 같은 상황이 반복되었습니다.그래서 혹시나 하는마음에 팀원한분에게 내가 아는 인증서 발급과정을 설명하며 진행해보았는데 이 분은 또 한번에 되었습니다.
  • 우리는 왜 이 부분이 왜 이렇게 되었고 어디서 실수가 있었는지 짚고 넘어가고싶은데 우리의 역량으로는 도저히 위 오류의 원인과 해결방법을 도저히 찾을수가 없었습니다.
Nginx CORS 이슈및 소켓끊김
  • 처음엔 그저 코드부분에서의 문제로만 생각하고 cors걸려있는 부분을 전부 모두허용으로 바꿔주었습니다.
  • 실패 후 아래와같이 cors 필터도 만들어보았습니다.

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CORSFilter implements Filter {@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

    @Override
    // CORS 설정
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Origin", "*");
        ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Methods","*");
        ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Headers","*");

        HttpServletResponse resp = (HttpServletResponse) servletResponse;

        if (request.getMethod().equals("OPTIONS")) {
            resp.setStatus(HttpServletResponse.SC_OK);
            return;
        }
        chain.doFilter(request, servletResponse);
    }

    @Override
    public void destroy() {

    }


}

  • 이래도 Cors는 해결되지 않았습니다. 이상한게 프론트쪽에서 서버가 연결되지도 않았는데 CORS가 발생해서 뭔가 설정쪽에 분명히 문제가 있다고 판단했습니다.
  • 구글링도해보고 찾아보다가 다른팀원이 nginx 에서 proxy 설정을 해보라고 했습니다.
  • 생각해보니 SSL인증을 받은 뒤에 WelcometoNginx가 나오면 끝이다 라고만 생각했었는데 CORS오류와 다른 팀원이 알려준 내용을 토대로 생각해보니 내 서버로 redirect가 되지 않고 있다는걸 발견했다. 우리는 애초에 서버가 켜지면 회원을 조회할수 있는페이지를 마련했었는데 WelcometoNginx가 나오는건 분명 문제가 있다는 거였던것이다. 사실 그냥 다된줄로만 알고 있었습니다. 그래서 nginx configuration을 건드려 보기로 했습니다.

$ sudo vim /etc/nginx/sites-available/default
  • 들어가면 아래화면에서 수정을 해주었습니다.

##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##
# Default server configuration
#
server {
        client_max_body_size 50M; << S3이미지 저장용량 제한해줘도 여기서 CORS걸려서 해줬음
        
        # SSL configuration
        #
        # listen 443 ssl default_server;
        # listen [::]:443 ssl default_server;
        #
        # Note: You should disable gzip for SSL traffic.
        # See: https://bugs.debian.org/773332
        #
        # Read up on ssl_ciphers to ensure a secure configuration.
        # See: https://bugs.debian.org/765782
        #
        # Self signed certs generated by the ssl-cert package
        # Don't use them in a production server!
        #
        # include snippets/snakeoil.conf;
        root /var/www/html;
        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;
        
        server_name xxxxx.shop www.xxxxx.shop; <<도메인넣어줘야함
        location / {
                   # First attempt to serve request as file, then
                   # as directory, then fall back to displaying a 404.
                   
                   #try_files $uri $uri/ =404; << 이거 기존에 있던거 주석처리했음
                   proxy_pass http:https://ec2아이피:8080; << redirect시켜줬음 이걸로인해 웰컴투안나옴
                   proxy_http_version 1.1;
                   proxy_set_header Upgrade $http_upgrade;
                   proxy_set_header Connection "Upgrade";
                   요기서 위에 3개는 소켓연결이 계속 끊겨서 넣음
                   
        }
        # pass PHP scripts to FastCGI server
        #
        #location ~ \.php$ {
        #       include snippets/fastcgi-php.conf;
        #
        #       # With php-fpm (or other unix sockets):
        #       fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
        #       # With php-cgi (or other tcp sockets):
        #       fastcgi_pass 127.0.0.1:9000;
        #}
        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #       deny all;
        #}
    #listen [::]:443 ssl ipv6only=on; # managed by Certbot  <<여기 주척처리해줬음.
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/nybae.shop/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/nybae.shop/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
#server {
#       listen 80;
#       listen [::]:80;
#
#       server_name example.com;
#
#       root /var/www/example.com;
#       index index.html;
#
#       location / {
#               try_files $uri $uri/ =404;
#       }
#}
server {
    if ($host = www.xxxx.shop) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    if ($host = xxxx.shop) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name contap.shop www.contap.shop;
    return 404; # managed by Certbot
}

  • 처음엔 proxy_pass 만 해줘서 우리가 이제 WelcometoNginx가 아닌 우리가 설정해놨던 페이지로 리다이렉트가 가능해졌다. 이와 동시에 1차적 CORS오류도 해결되었습니다.

  • WebSocket을 지원할 때 리버스 프록시 서버가 직면하는 몇 가지 문제가 있습니다.
  • 하나는 WebSocket이 hop-by-hop 프로토콜이므로 프록시 서버가 클라이언트의 Upgrade 요청을 가로챌 때 적절한 헤더를 포함하여 WAS 서버에 업그레이드 요청을 보내야 한다는 것 입니다.
  • 또한 HTTP의 단기 연결과 달리 WebSocket은 오래 지속되기 때문에, 리버스 프록시는 연결을 닫지 않고 열린 상태로 유지하는 것을 허용해야 합니다.
  • 소켓에선 CORS가 나타나진 않았고, 지속적으로 연결이 끊기는 현상이 발생했습니다. 그래서 우리 서비스의 소켓과 관련된 실시간알람,채팅 들이 먹통이 되어버렸습니다.
# Web-socket 관련 설정들

# 1. HTTP/1.1 버전에서 지원하는 프로토콜 전환 메커니즘을 사용합니다.
proxy_http_version 1.1;

# 2. hop-by-hop 헤더를 사용합니다.
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 3. 받는 대상 서버(WAS)
#proxy_set_header Host $host;

  • Nginx는 클라이언트와 WAS 간 터널(소켓)을 설정할 수 있도록 WebSocket을 지원합니다. NGINX가 클라이언트에서 WAS로 업그레이드 요청을 보내려면 Upgrade 및 Connection 헤더를 명시적으로 설정해야 합니다.
  • 위와같이 작성하니 해결되었습니다!!!!
  • 하지만 또 2차적인 문제가 생겼습니다.
  • 이미지 업로드 문제였는데 우리는 백이 S3에서 이미지를 관리 하기로 했었습니다.
  • S3에서 아무리 이미지 용량제한을 늘려준다고 하더라도 nginx에서 설정을 따로 제한을 두지 않는 이상 이미지가 1MB이상이면 업로드를 실패하는 현상이 발생하였습니다.
  • 이건 마침 이 오류를 인지하지도 못할때 아까 도움을 받았던 팀원이 이미지 파일도 문제가있기에 nginx configuration 설정을 하시면 해결이 될거다 라고 알려 준 덕분에 빠르게 캐치할 수 있었습니다.
server {
listen       80;
listen 443 ssl http2;
srever_name www.도메인;
client_max_body_size 용량M;

  • 위의 설정내용에서 제일 맨위에 내용을 위와같이 추가해줘서 2차적인 문제도 말끔하게 해결되었습니다.
메세지 저장방식
  • 위의 사진은 데이터베이스에 데이터를insert하는 API 요청의 런타임 입니다. (첫번째 api의 경우는 회원가입요청이라 비밀번호를 암호화 하는 과정에서 시간이 더 걸렸을거라 예상합니다. 두번째 api는 단순한 구조를 갖고 있는 테이블에 데이터를 넣는 것이었습니다.) 저희팀 백엔드 개발자들은 ARC나 PostMan을 사용하면서 공통적으로 느낀 점은 데이터베이스에 insert하는 작업이 데이터를 조회하는 것보다 시간이 오래걸린다는 것이었습니다.

  • 위와 같은 생각을 지닌 상태로 채팅기능을 구현했기 때문에 1초에도 몇십개의 메시지가 발생 할 수 있는 서비스에서 메시지가 발생할때마다 DB에 insert를 하는 행위는 하면 안되겠다는 생각하였습니다. 그치만 구글링을 해봐도 어떤식으로 저장해라 라는 명확한 글을 못 찾았습니다. 그냥 하나씩 저장하는 방식은 옳지못하다는 글 뿐이었습니다.

  • 그래서 고민하다가 저희조에서 채용한 메시지 저장 방법은 메시지가 발생할때마다 서버에서 해당 메시지를 메모리에 갖고 있습니다. 갖고 있는 메시지의 개수가 100개가 넘으면 100개의 데이터를 한번에 저장하도록 구현했습니다. 사실 위와 같은 방법이 옳은 방법인지는 잘 모르겠습니다. 데이터가 저장되는과정에서 에러가 발생한다면 치명적인 문제가 발생할 것 같다는 생각이 들면서도 아직까지는 큰 문제가 없고 다른 좋은 방법도 떠오르지 않아 사용하고 있습니다. 더 좋은 방법이 있다면 알려주시면 감사하겠습니다..!

기본적인 채팅기능
  • 단순 채팅기능(1:1채팅)까지만 구현한 후에 저희조는 채팅을 구현했으면 기본적으로 있어야 할 기능들을에대해서 고민을 해봤습니다. 그래서 나온 결론은 아래 3가지가 기본적으로 있어야 한다고 생각했습니다.

         1. 알람기능.
    
         2. 채팅방 조회시 최신순으로 정렬하는 기능.
    
         3. 채팅방 조회시 새로운 메시지가 있음을 알리는 기능.
    
  • 위의 기능은 메시지가 발생할때마다 DB에있는 값을 바꿔주는 행위를 해야지 가능한 기능들입니다(저희가 알고 있는 지식 내에서 내린 결론입니다.) 하지만 채팅 메시지 저장 방식에서 고민했듯이 저희 조는 메시지가 발생할때마다 DB에 값을 insert하거나 update하는 방식을 선호하지 않았고 다른 좋은 방식이 있을지 고민을 많이 했었습니다. 그러던 중 이바울 멘토님께서 Redis의 key/value기능에 대해서 얘기해주셨습니다. redis에는 key/value 물론이고 정렬까지 해주는 자료구조까지 갖고 있어서 저희가 Redis를 잘 사용한다면 메시지가 발생할때마다 데이터 베이스에 접근하지 않아도 위의 기능을 구현할수있겠다고 생각했습니다. 저희조는 바로 Redis를 공부했고, 그를 바탕으로 위의 기능들을 구현했습니다. 전체적인 로직은 아래와 같습니다.

사용자 권한 비트 연산
  • 저희조는 계속되는 요청에 DB에 단순한 Boolean형태 데이터가(알람설정과 같은 정보) 컬럼으로 추가됨으로써 , 칼럼을 많이 생성하는 것은 비효율적이라 생각되었습니다.
  • 그래서 비트연산을 사용하면 여러가지 Boolean 데이터를 한 칼럼안에 저장 할 수 있기 때문에 여러가지 Boolean 데이터를(최대 32개) 하나의 int형으로 저장하는 방식으로 구현하였습니다.

  • Boolean 형태 컬럼을 추가하며 권한을 관리 할 경우
@Entity
User {	
		@Column
    private Boolean phoneTutorial;

    @Column
    private Boolean profileTutorial;

    @Column
    private Boolean otherUserRead;

    @Column
    private Boolean alarm;
}


//Service
public class MainService {
	//사용자 알람 여부 권한 bit연산으로 관리
	public void changeAlarmState(int alarmState, User user) {
        user.setAuthStatus(!authStatus);
        userRepository.save(user);
    }
}

  • 비트연산으로 값을 권한을 관리할 경우
@Entity
User {
			@Column
	    @Schema(description = "사용자 권한(bit로 관리함) 0001:폰,0010:프로필,0100:otherUserRead,1000:alarm")
	    private int authStatus;
	}


//Enum ->비트연산
public enum AuthorityEnum {
		PHONE_TUTORIAL(Authority.PHONE_TUTORIAL),
    PROFILE_TUTORIAL(Authority.PROFILE_TUTORIAL),
    CAN_OTHER_READ(Authority.CAN_OTHER_READ),
    ALARM(Authority.ALARM),
    ALL_AUTHORITY(Authority.ALL_AUTHORITY);

		public static class Authority {
        public static final int PHONE_TUTORIAL = 0b0001;
        public static final int PROFILE_TUTORIAL = 0b0010;
        public static final int CAN_OTHER_READ = 0b0100;
        public static final int ALARM = 0b1000;
        public static final int ALL_AUTHORITY = 0b1111;
    }
}

//Service
public class MainService {
	//사용자 알람 여부 권한 bit연산으로 관리
	public void changeAlarmState(int alarmState, User user) {
        int authStatus = user.getAuthStatus();
        if(alarmState==0) {
            authStatus = authStatus & (AuthorityEnum.ALL_AUTHORITY.getAuthority() - AuthorityEnum.ALARM.getAuthority());
        }
        else if(alarmState==1) {
            authStatus = authStatus|AuthorityEnum.ALARM.getAuthority();
        }
        user.setAuthStatus(authStatus);
        userRepository.save(user);
    }
}
기존 예외 로그를 저장하는 방식의 단점
  • FrontEnd가 배포시에는 Console을 찍은 내용들을 다 지워야 한다고 했습니다.
  • 그렇게되면 프론트측에선 그 이외의 예외들을 메세지만 확인할 수 있고 정확하게 어떤 오류인지 파악이 힘들었습니다. (BackEnd도 nohup으로 배포를 진행해 로그를 확인하는데 있어 어려움이 있었습니다.)
  • 그래서 처음엔 log를 메모장형식으로 저장되게끔 xml을 이용해서 남겼었습니다.
  • 하지만 이게 적으면 상관없겠지만 로그에 찍힌 내용이 많아졌을때는 찾는게 너무 힘들었습니다.
    • 기존 로그를 남겼던 방식
  • 그래서 Sentry를 이용해 BackEnd가 예외처리한 내용들 이외의 것들을 체크하고 좀더 수월하게 Fix할 수 있게 되었습니다.
  • 예시로 최종 직전 회원가입이 안되는 이슈가 발생했는데 프론트측에는 서버에 문의해주세요 라는 우리가 정한 메세지만 보여졌고 우리 또한 nohup으로 배포중이었기에 로그 확인이 어려웠습니다.
  • 하지만 Sentry 페이지에 들어가 어떤 이슈인지 한눈에 파악을 할 수 있었으며 위의 이슈를 5분도 걸리지 않고 바로 수정이 가능했습니다.
  • Sentry는 그냥 단순 이슈만 보여주는게 아닌 같은이슈가 몇번 발생했는지도 파악할 수 있으며,FrontEnd에서 요청한 API의 속도,총 요청한 API호출 대비 실패율 등의 내용을 확인할 수 있습니다.
  • 하지만 JSON BODY에 있는 값들은 확인이 아직까지는 불가능했기에 정확하게 이슈가 어떤 값으로 인해서 발생했는지 는 파악이 힘들었습니다. 이게 사용방법 미숙으로 인한건지 실제로 BODY값은 보여지지 않는건지 확인이 필요한 상황입니다.
성능 테스트
  • 저희 백엔드 개발자들이 성능적으로 걱정한 부분은 그랩목록(친구 목록)불러오기 입니다.(이것 외에도 많긴합니다..) 왜냐하면 그랩목록을 불러올때 가장 최근에 메시지를 주고받은,아니면 가장최근에 새롭게추가된 그랩들 순으로 불러와야하는데 이와같이 하려면 아래와 같은 절차를 밟아야 합니다.
여기서 그냥 데이터베이스에 최신날짜를 계속해서 업데이트 해주고 orderBy를 이용해서 정렬한데이터를 주면 되는것 아니냐 라는 의문을 갖으시는 분들이 계실것같은데, 저희조는 새 메시지가 발생할때마다 데이터베이스에 해당시간을 저장하는 방식이 아니라, 레디스에 저장하고 있습니다.)
  • 위와같은 다른 읽기 쿼리문보다 조금은(?) 복잡한 절차를 거치기 때문에 성능적으로 문제가 되진 않을지 걱정했었고,100명의 친구목록이 있는 User로 Test를 그랩목록을 12개씩 불러오는 테스트의 경우 평균적으로 16.77ms가 나왔고, 그랩목록 전체를 불러오는 경우(100개) 100ms가 나왔습니다.

User FeedBack

  • FeedBack 통계


  • FeedBack - 카드작성시 뭘 해야할지 잘 모르겠습니다, 카드형식으로 프로젝트를 보여주기 때문에 제약이 많았습니다.
  • Solution - 기획단계부터 Closed Community형식으로 가기위해 뒷면카드를 작성하지 않으면 다른사람의 카드를 열람하시 못하게 하였으나 그렇게하니 카드 작성을 어떤식으로 해야할지 모르겠다는 피드백이 많았는데 이때 떠오른 해결방법은 두가지 였습니다.
    1. ClosedCommunity를 유지하고 온보딩 형식으로 카드작성 가이드를 보여준다.
    2. 뒷면카드 열람권한을 로그인만 하면 가능하게 하여 사용자가 직접 카드를 탐색하여 작성할 수 있게 한다.
    • 전자의 방법은 너무 강제성이 강할 것 같았고 아무래도 하나하나 설명하는 사이트가 좋은 사이트처럼 보이지는 않아서 PlaceHolder로 최대한 상세하게 알려주는 것을 전제로 해결방안으로 후자를 선택하게 되었습니다.
  • FeedBack - 백엔드,프론트엔드의 색이 구분이 되었으면 좋겠다.
  • Solution - 핵심 기능이 개발자와 디자이너의 매칭이기 때문에 개발자 끼리의 색 구분은 크게 의미가 없다고 생각 했었으나 생각보다 많은 피드백 요청이 와서 많은 고민을 했었습니다.색을 추가만하면되는 간단한 작업이었지만 이 부분 또한 사이트의 메인컬러라고 생각할 수 있기 때문에 정말 신중하게 색상을 선택하여 적용했습니다.

Marketing

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Java 95.3%
  • HTML 3.9%
  • Shell 0.8%