이번엔 마이페이지로 이동해서 , 그곳에서 회원 활동로그와 작성한 게시글,댓글,스크랩 목록을 볼 수 있도록 구현했다.
OKKY 사이트처럼 다른사람도 그 정보를 닉네임을 클릭하는 등의 행위를 통해서 볼 수 있도록 만들었다.
다른 커뮤니티처럼 내 정보는 따로 검색을 하거나 등을 제외하면 나만 볼 수 있도록 할까... 생각을 했지만
IT커뮤니티이니 만큼 (?) 이상한 짓을 못하도록 그냥 모든 정보를 회원들이 서로 볼 수 있도록 만들었다.
실제로 나도 OKKY 커뮤니티를 사용하면서 다른 커뮤니티들과 다른 그런 정보공개?가 처음에는 낯설었지만
꽤나 괜찮다는 생각을 받기도 했다.
단, 너무 행동에 조심하게? 되는 부분에 대해 게시글과 댓글을 삭제하면 활동로그가 아예 삭제되도록 구현해서
해당 부분을 보완했다.
일단 해당 기능 또한 검색을 해도 따로 힌트나 도움이 될 만한 정보가 나오진 않았다.
그래서 내 생각대로 만들었는데 , 부족한 실력이다보니 꽤나 하드코딩이 된 것 같아서 마음이 아프다,,,
먼저 okky의 마이페이지 디자인을 따라서 구현해봤고 (허접함 주의)
해당페이지에서 최근활동, 게시물 , 스크랩을 클릭했을때
주소창에서 회원의 PR은 유지되면서 뒤의 주소창에 변화가 일어나는 부분을 보고,
get 방식으로 회원PR받으며, PR기준으로 select문을 구별해서 구현하면 되겠구나하는 생각이 들었다.
구현 로직을 먼저 설명하자면
- 회원의 활동로그를 저장 할 테이블을 생성
- 글작성,좋아요,싫어요,DEV,댓글작성,채택 등의 행위를 할 때 insert 되도록 코드 추가
- 반대로 글삭제,좋아요취소,싫어요취소 등의 행위를 할 때에는 따로 기록을 남기지 않기 위해 delete 되도록 추가
- insert시에는 활동로그 테이블에 카테고리별로 구분되어 어떤 활동을 했는지 기록됨
- 활동로그 테이블과 게시판 테이블 , 댓글테이블을 join을 해서 List로 출력
- 뷰에서 List의 카테고리 컬럼을 기준으로 if문을 통해 출력 될 문장을 나눠줌
- 내 게시글, 내 댓글 , 내 스크랩을 출력하기 위해 select문을 위와 동일하게 List로 가져와서 출력
- 활동로그 , 게시글 , 댓글 , 스크랩은 get 방식을 사용해서 주소창에서 "/주소?select=" 로 나눠주고
- 어떤 회원의 프로필을 볼 건지는 "/주소?select=# &memberId=#"으로 나눠준다.
'1234'회원의 활동기록이다.
아직 추가적으로 디자인을 좀 더 깔끔하게 가꾸고 , 페이징 기능을 추가해야하지만 1차적으로 필요한 기능은 구현됐다.
주소창 부분에 select=log ( 활동기록 ) , memberId=1234 (회원PR) 로 나누어서 출력한다.
활동기록 테이블 생성
create table MP_LOG (
LOGNO number not null,
MEMBER_ID varchar2(50) not null,
bno number,
rno number,
questionid varchar2(50),
categori number ,
logdate date default sysdate,
constraint log_pk PRIMARY key (LOGNO),
constraint log_fk FOREIGN key (MEMBER_ID)
references MP_MEMBER(MEMBER_ID) on delete cascade
);
LogNO
Member_ID ( 회원PR )
Bno ( 게시글PR )
Rno ( 댓글PR )
Questionid (채택받은 사람의 id인데 설계시에는 추가했지만 구현하고보니 rno를 기준으로 join하면 값이 나오기 때문에 생략해도 무방함)
Categori ( 어떤 활동인지 나누는 기준 )
logDate ( 정렬용 활동기록 시간)
회원 ID가 삭제되면 같이 정보가 삭제되도록 delete cascade를 해줬다.
Mapper.xml
<insert id="insertLog" parameterType="kr.co.vo.LogVO">
insert into MP_LOG (LOGNO , MEMBER_ID,BNO,RNO,QUESTIONID,CATEGORI)
values
((SELECT NVL(MAX(LOGNO), 0) + 1 FROM MP_LOG), #{memberId}, #{bno} , #{rno, jdbcType=INTEGER} , #{questionId, jdbcType=VARCHAR},#{categori})
</insert>
먼저 insert이다
게시글 작성등의 활동에는 rno가 들어가지 않기 때문에 jdbcType을 사용해서 null값을 허용해주자.
(mybatis 사용시 insert에 null값이 들어가면 nullpoint에러 발생)
<delete id="deleteLog" parameterType="kr.co.vo.LogVO">
delete from MP_LOG where MEMBER_ID = #{memberId} and BNO = #{bno} and CATEGORI = #{categori}
</delete>
<delete id="deleteReplyLog" parameterType="kr.co.vo.LogVO">
delete from MP_LOG where MEMBER_ID = #{memberId} and RNO = #{rno} and CATEGORI = #{categori}
</delete>
게시글 삭제 , 게시글 추천 취소 , 댓글 삭제 , 댓글 추천 취소 등에 들어갈 delete 문이다.
게시글PR , 댓글PR기준이기 때문에 두개의 delete문을 만들어줬다.
로그 테이블에 회원PR을 기준 , BNO에 해당하는 게시글의 활동(Categori로 구분)을 삭제한다.
글 작성은 bno로 구분되고 ,
글 추천 등의 행위는 회원당 1번으로 제한했기 때문에 카테고리를 기준으로 삭제해도 문제가 없다.
Mapper를 작성했으니 VO를 작성해서 편하게 파라미터를 받아보자.
public class LogVO {
private String memberId;
private int bno;
private int rno;
private String questionId;
private int categori;
private Date logdate;
private BoardVO boardVO;
private ReplyVO replyVO;
//getter&setter 생성
}
jojn 쿼리때에 사용 할 게시판VO와 댓글VO도 추가해준다.
이제 글작성 , 글삭제 등의 로직에 활동기록 테이블에 insert되도록 추가해준다.
이때 난 service에 넣어줬는데 처음에 잘 못 된 설계와 구현방법으로 인해 controller에서
service에 구현해야 할 항목들을 그냥 Controller에서 구현했다.
후에 로직을 여러가지 추가하려고 하니 결합성이 강해져서 고생이 늘어버렸다.
앞으론 Controller에선 파라미터를 받고 , 간단한 구분정도만 해주기로 하고
Service에서 컨트롤러에서 받은 파라미터로 로직을 작성하도록 할 예정이다.
(Controller에서 로직을 작성하는 바람에 받은 매개변수를 두,세번씩 선언하는 등 코드도 길어지고 복잡해졌다)
ServiceImpl
1. 게시글 작성
2. 게시글 좋아요
3.게시글 싫어요
4.게시글 dev
5.게시글 스크랩
insert bno
6.댓글 작성
7.댓글 좋아요
8.댓글 싫어요
9.댓글 dev
insert rno
10. 채택
bno rno 채택한사람ID 채택받은사람ID
11.채택받음
bno rno 채택받은사람ID
insert questionId
1.게시글 삭제
2.게시글좋아요취소
3.게시글싫어요취소
4.게시글 dev취소
5.게시글스크랩 취소
deletelog
6.댓글삭제
7.댓글좋아요취소
8.댓글싫어요취소
9.댓글dev취소
deleteReplyLog
이런게 11가지의 카테고리의 로직에 insert와 delete를 추가하면서 22번의 코드를 작성했고 ,
파라미터의 잘못된 설계로 경로를 여러번 수정하고 , 추가하는 등 꽤나 시간이 걸렸다.
여려운 작업은 아니였지만 시간이 참 오래걸렸다.
insert, delete 로직에 logVO 객체를 가져와서 set으로 값을 넣어줬다.
만약 추가하고 싶은 활동로직이 있다면 카테고리를 새로 구분해서 해당 기능로직에 추가해주면 되겠다.
(로그인 기록 , 프로필 사진 변경 , 닉네임 변경기록 등등등)
이제 회원의 활동로직 사이에 테이블에 insert , delete가 제대로 되도록 구현했다면
select로 값을 뷰에 뿌려주면 된다.
Mapper.xml
<select id="memberInfo" resultMap="memberVO">
select MEMBER_NAME , MEMBER_IMG , MEMBER_POINT , MEMBER_DEVPOINT from MP_MEMBER where MEMBER_ID = #{memberId}
</select>
<select id="memberLog" resultMap="LogVO">
select a.BNO , a.RNO ,a.member_id ,a.QUESTIONID , a.CATEGORI , a.LOGDATE , b.title,b.writer ,b.bgno,c.writer from MP_LOG a
left outer join MP_BOARD b on a.bno = b.bno
left outer join MP_REPLY c on a.rno = c.rno
where a.member_id = #{memberId} order by a.logdate desc
</select>
<select id="memberWrite" resultType="kr.co.vo.BoardVO">
select BNO , TITLE ,WRITER , REGDATE , LIKEHIT , HATEHIT , DEVHIT ,BGNO from MP_BOARD where ID = #{memberId} order by regdate desc
</select>
<select id="memberReply" resultMap="ReplyVO">
select b.bno , b.title , b.bgno , b.writer , a.regdate , a.rno from MP_Reply a left outer join MP_Board b
on a.bno = b.bno
where a.ID = #{memberId} order by a.regdate desc
</select>
<select id="memberScrap" resultMap="BoardVO">
select b.BNO , b.title , b.regdate , b.likehit , b.hatehit , b.devhit , b.bgno, a.scrapdate from MP_SCRAP a right outer join MP_BOARD b
on a.bno = b.bno where a.member_id = #{memberId} order by a.scrapdate desc
</select>
일단 5개의 쿼리문들에 필요한 파라미터는 오직 memberId ( 회원PR ) 이다.
해당 파라미터를 받아오는 방식은 뒤에 작성하겠다.
첫번째 memberInfo 는
회원의 프로필사진 , 닉네임 , 좋아요Point , 개발Point 를 가져와서
회원의 프로필을 작성하는데에 사용한다.
2~5는 활동기록 , 작성한게시글 , 댓글 , 스크랩한 게시글 이다.
join을 이용한 간단한 select문이기 때문에 따로 설명은 생략하겠다.
Controller
@RequestMapping(value="/board/mypageView", method=RequestMethod.GET)
public String mypageView(String select,String memberId,Model model) throws Exception{
logger.info("select"+select);
model.addAttribute("info",service.memberInfo(memberId));
model.addAttribute("memberId", memberId);
if(select.equals("log")) {
model.addAttribute("log",service.memberLog(memberId));
}else if(select.equals("write")) {
model.addAttribute("write",service.memberWrite(memberId));
}else if(select.equals("scrap")) {
model.addAttribute("scrap", service.memberScrap(memberId));
}else if(select.equals("reply")) {
model.addAttribute("reply", service.memberReply(memberId));
}
return "/board/mypageView";
}
일단
마이페이지 뷰인 mypageView를 호출하면 get방식으로 받는다.
매개변수를 @RequestParam을 통해 select , memberId 를 받아준다 ( 생략 )
프로필을 위한 info , 페이지 내에서 카테고리 이동을 위한 memberId를 model을 이용, 뷰로 데이터를 보내주고
if문으로 select값에 따라 해당 값에 맞는 쿼리를 구분해서 List를 뷰로 보내주도록 한다.
뷰에선 forEach문을 사용하기 때문에 log , write , scrap , reply List들의 코드를 작성해도 데이터 값이 없으면
출력되지 않는다. ( 해당되는 데이터값만 출력됨 )
이제 뷰단 출력인데 , 모자란 실력으로 엄청난 하드코딩을 해버렸으니 난해함에 주의바란다. ( 스승님이 있었으면,,,)
mypageView.jsp
<button type="button" class="btn btn-dark ml-3" onclick="location.href='/board/mypageView?select=log&memberId=${memberId}'">활동기록</button>
<button type="button" class="btn btn-dark" onclick="location.href='/board/mypageView?select=write&memberId=${memberId}'">작성한 게시글</button>
<button type="button" class="btn btn-dark" onclick="location.href='/board/mypageView?select=reply&memberId=${memberId}'">작성한 댓글</button>
<button type="button" class="btn btn-dark" onclick="location.href='/board/mypageView?select=scrap&memberId=${memberId}'">스크랩</button>
일단 버튼을 만들어준다.
사용자는 본인의 마이페이지 버튼 클릭이나, 닉네임을 클릭해서 해당 페이지에 접속이 가능한데,
그때 자동적으로 memberId값을 가져오게 된다.
그래서 다른 버튼을 누를때 memberId를 매개변수로 받고 , 다시 뷰로 전송해주면서 memberId값을 유지했다.
(컨트롤러의 model.addAttribute(memberId) )
<c:forEach items="${log}" var="log">
<div class="col-sm-9">
<div class="card shadow mt-2 mb-1">
<!-- <i class="fa fa-comment-o"></i> -->
<div class="row ml-1">
<span>
<span style="font-size: 20px">
<c:if test="${log.categori == 1 }">
<i class="fas fa-edit mr-2" style="color:blue"></i>
#${log.bno} 게시글을 작성 했습니다.
</c:if>
<c:if test="${log.categori == 2 }">
<i class="far fa-thumbs-up mr-2" style="color:red"></i>
#${log.bno} 게시글을 추천 했습니다.
</c:if>
<c:if test="${log.categori == 3 }">
<i class="far fa-thumbs-down mr-2" style="color:dark"></i>
#${log.bno} 게시글을 반대 했습니다.
</c:if>
<c:if test="${log.categori == 4 }">
<i class="fa fa-bolt mr-3" style="color:yellow"></i>
#${log.bno} 게시글을 DEV 했습니다.
</c:if>
<c:if test="${log.categori == 5 }">
<i class="fas fa-check-double mr-2" style="color:green"></i>
#${log.bno} 게시글을 스크랩 했습니다.
</c:if>
<c:if test="${log.categori == 6 }">
<i class="fa fa-comment-o mr-2" style="color:blue"></i>
#${log.bno} 게시글에 #${log.rno} 댓글을 작성 했습니다.
</c:if>
<c:if test="${log.categori == 7 }">
<i class="far fa-thumbs-up mr-2" style="color:red"></i>
#${log.bno} 게시글에 #${log.rno} 댓글을 추천 했습니다.
</c:if>
<c:if test="${log.categori == 8 }">
<i class="far fa-thumbs-down mr-2" style="color:dark"></i>
#${log.bno} 게시글에 #${log.rno} 댓글을 반대 했습니다.
</c:if>
<c:if test="${log.categori == 9 }">
<i class="fa fa-bolt mr-3" style="color:yellow"></i>
#${log.bno} 게시글에 #${log.rno} 댓글을 DEV 했습니다.
</c:if>
<c:if test="${log.categori == 10 }">
<i class="fa fa-bolt mr-3" style="color:red"></i>
#${log.bno} 질문글에 #${log.rno} 댓글 답변자 ${log.questionId}님을 채택 했습니다.
</c:if>
<c:if test="${log.categori == 11 }">
<i class="fa fa-bolt mr-3" style="color:red"></i>
#${log.bno} 질문글에 #${log.rno} 댓글이 채택 되었습니다.
</c:if>
</span>
<span style="font-size: 13px"><fmt:formatDate value="${log.logdate}" pattern="YY-MM-dd hh:mm:ss" /> </span>
</span>
</div>
<div class="ml-5">
<h5>
<a href="/board/readView?bno=${log.bno}&bgno=${log.boardVO.bgno}">${log.boardVO.title}</a>
<span style="font-size: 15px"> ${log.boardVO.writer} </span>
</h5>
</div>
</div>
</div>
</c:forEach>
활동기록의 부분이다 ( select = log )
if문을 이용해 log.categori로 카테고리 값을 가져오고 , 카테고리값을 이용해서 문장을 바꿔준다.
<c:forEach items="${write}" var="write">
<div class="col-sm-8">
<div class="card shadow mt-1 mb-1">
<!-- <i class="fa fa-comment-o"></i> -->
<div class="row ml-1">
<span>
<span style="font-size: 20px">
<i class="fas fa-edit mr-2" style="color:blue"></i>
#${write.bno} 게시글을 작성 했습니다.
</span>
<span style="font-size: 13px"><fmt:formatDate value="${write.regdate}" pattern="YY-MM-dd hh:mm:ss" /> </span>
</span>
</div>
<div class="ml-5">
<h5>
<a href="/board/readView?bno=${write.bno}&bgno=${write.bgno}">${write.title}</a>
<span style="font-size: 15px"> ${write.writer} </span>
</h5>
</div>
</div>
</div>
</c:forEach>
작성한 게시글 부분이다. 카테고리가 필요 없기 때문에 간단하게 작성이 가능하다.
댓글과 , 스크랩의 경우도 이와 같기 때문에 생략한다.
결과
- '1234' 회원의 활동로그
- '1234' 회원의 작성한 게시글
- '2222' 회원의 활동로그
- '2222' 회원의 작성한 게시글
(주소창의 get방식을 이용해서 해당 기능을 구현)
구현하면서 여러번 중복된 값이 DB에 올라갔기 때문에 , 결과가 조금 이상할 수 있지만 제대로 구분됨.
프로필 사진은 아직 구현 안했기 때문에 , 모두 같은 것임 ( 구현예정 )
이번엔 마이페이지를 구현했다.
아직 페이징도 추가해야하고 디자인도 다듬어야하지만 구현완료이다.
프로필 사진은 일단 나중에 구현해야지 하고 미뤄뒀는데 지금와서는 큰 짐이 되었다.. (반성...)
회원 프로필 사진 입력 , 수정을 구현하고 목록,상세내용,마이페이지,댓글 등등.. 추가해야 할 곳이 참 많다..
미리 구현했으면 흐름대로 할 수 있었을 텐데 했던거 또하고 또하고..
프로젝트 코드가 늘어날 수록 노가다가 늘어나고 있다.
참 설계와 일 분담의 중요성을 느끼게 되는 요즘이다.
취업을 해서 실제로 서비스를 구현하게 된다면 , 꼭 이런 효율성에 대한 부분을 생각하며 일해서 작업시간을 단축하고
코드를 가독성이 좋게 작성할 수 있도록 힘써야 겠다.. ( 머리가 나쁘면 몸이 고생 ㅜ)
앞으로 구현 할 목록
마이페이지- 프로필사진
- 관리자페이지
- 채팅 , 알림기능 ( 소켓이용 )
- 오픈 API를 이용한 구글로그인 , 지도 이용
'SPRING > IceWater Community' 카테고리의 다른 글
[스프링] 메인페이지,인기글 구현 (블라인드 참고) (0) | 2021.10.17 |
---|---|
[스프링] 회원 프로필사진 수정 , 등록 (3) | 2021.10.14 |
[스프링]게시판 스크랩기능 구현 (3) | 2021.10.11 |
[스프링] 질문 채택 기능 구현 (Like 지식 IN) (0) | 2021.10.08 |
[스프링] 댓글 좋아요/싫어요 구현 - (Ajax) (3) | 2021.10.06 |