환경) sts3 , MySQL , MyBatis
게시물을 조회하고 해당 게시물을 저장할 수 있는 찜하기 기능을 구현하였다.
시큐리티를 적용시킬 경우 테스트하는데 복잡하기 떄문에 우선 임의로 로그인 사용자를 지정해서 하였다.
해당 기능의 로직은 다음과 같다.
1. 상세페이지의 찜하기 버튼을 클릭한다.
-> 나는 fontAwesome에 있는 아이콘을 이용해서 만들었다.
2-1. 로그인한 사용자가 해당 게시물을 이미 찜한 상태라면 alert창을 생성
2-2 찜하지 않은 상태라면 confirm을 통해 찜리스트 페이지로 들어갈지 말지 결정.
3. 찜리스트 페이지에 찜한 게시물의 정보가 출력된다.
* 구현코드 및 사진
1. DB
간단하게 사용자(userid)와 게시물번호(Cno - [PK])만 받아서 설계하였다.
정답은 아니겠지만 일단 내가 찜한 게시물의 정보를 얻어와야하기 떄문에 게시물의 정보를 가진 데이터 중
기본키인 cno 를 가지고 얻어오는 것이 맞다고 판단,
정규화의 성격에도 알맞기 때문이다.
2. VO & Mapper (XML)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LikeVO {
private String userid;
private Long cno;
}
public interface LikeMapper {
// 찜하기 ( 추가 )
public int insert(LikeVO vo);
// 찜 목록 확인
public List<LikeVO> getLikeListByUserid(String userid);
// 찜 목록 1개 삭제
public boolean delete(@Param("cno")Long cno, @Param("userid")String userid);
// 게시물삭제시 전체삭제
public boolean deleteAllByCno(Long cno);
// 이미 찜한 게시물인지 조회
public LikeVO getByCnoWithUserid(@Param("vo")LikeVO vo);
}
<mapper namespace="com.cs.mapper.LikeMapper">
<resultMap type="com.cs.domain.LikeVO" id="likeMap">
<result property="userid" column="userid"/>
<result property="cno" column="like_cno"/>
</resultMap>
<insert id="insert" parameterType="com.cs.domain.LikeVO">
insert into tbl_like ( userid , like_cno ) value ( #{userid} , #{cno} )
</insert>
<select id="getLikeListByUserid" resultMap="likeMap" resultType="com.cs.domain.LikeVO">
select * from tbl_like where userid = #{userid}
</select>
<delete id="delete">
delete from tbl_like where userid = #{userid} and like_cno = #{cno}
</delete>
<delete id="deleteAllByCno">
delete from tbl_like where like_cno = #{cno}
</delete>
<select id="getByCnoWithUserid" resultMap="likeMap" resultType="com.cs.domain.LikeVO">
select * from tbl_like where like_cno = #{vo.cno} and userid = #{vo.userid}
</select>
</mapper>
LikeVO의 property들과 DB 테이블의 column의 이름이 다르기 때문에 resultMap으로 해당 필드들을 매핑시켜준다.
나머지 설명은 어떤식으로 구성된 것인지 이해하실거같으니 패스.
3. Service 계층
public interface LikeService {
public int register(LikeVO vo);
public List<ClothesVO> getLikeListByUserid(String userid);
public boolean remove(Long cno, String userid);
public LikeVO getByCnoWithUserid(LikeVO vo);
}
@Service
@Log4j
@RequiredArgsConstructor
public class LikeServiceImpl implements LikeService {
private final LikeMapper likeMapper;
private final ClothesMapper clothesMapper;
private final ClothesAttachMapper clothesAttachMapper;
@Transactional
@Override
public List<ClothesVO> getLikeListByUserid(String userid) {
// 1. 사용자가 찜한 목록을 찾는다
List<LikeVO> likeList = likeMapper.getLikeListByUserid(userid);
// 2. 찜한 게시물의 정보를 담을 그릇을 만든다.
List<ClothesVO> clothesList = new ArrayList<ClothesVO>();
// 3. 찜한 목록이 없으면 null을 반환.
if(Objects.isNull(likeList)) {
return null;
}
// 4. 찜한 목록을 DB를 하나씩 조회한다.
likeList.forEach(i -> {
// 4.1 해당 사용자가 찜한 게시물의 번호로 게시물과 첨부파일 정보를 조회한다.
ClothesVO vo = clothesMapper.read(i.getCno());
List<ClothesAttachVO> attachList = clothesAttachMapper.findByCno(i.getCno());
// 4.2 첨부파일이 없으면 그냥 add
if(Objects.isNull(attachList) || attachList.size() == 0) {
log.warn("No have Attach By Cno : " + i.getCno());
} else {
// 4.3 첨부파일이 있으면 대표이미지의 경로를 썸네일Url로 설정
ClothesAttachVO attachVO = clothesAttachMapper.findByCno(i.getCno()).get(0);
vo.setThumbnailUrl(makeThumbnailURL(attachVO));
log.warn("make ThumbnailUrl");
}
clothesList.add(vo);
});
return clothesList;
}
// 4.4 썸네일 생성
private String makeThumbnailURL(ClothesAttachVO attachVO) {
String url = null;
try {
url = URLEncoder.encode(attachVO.getUploadPath() + "/s_"
+ attachVO.getUuid() + "_" + attachVO.getFileName(),"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return url;
}
}
다른 메서드들은 일단 생략하고 getLikeListByUserid 메서드가 가장 중요하고 로직이 추가되는 부분이기 때문에
설명을 해보면
특정 사용자(admin44)가 3개의 게시물(1,2,3)을 찜했다고 했을 떄 Like 테이블 DB에는 3개의 Row가 저장돼있다.
각 row는 admin-1 , admin-2 , admin-3 이 되어있으므로 cno 칼럼의 값을 뽑아내서
해당 번호의 게시물의 정보와 첨부파일을 찾아내서 반환할 List 컬렉션에 추가하는 형태이다(4.4 - 4.5번 작업)
4. 컨트롤러 계층
해당 서비스계층에서 사용한 getLikeListByUserid를 통해서 반환되는 List를 화면으로 전송합니다.
이때 Model을 사용해서 데이터를 넘겨줘도 되며
위 코드처럼 Ajax통신을 사용하기위해서 @ResponseBody를 사용해 넘겨줘도 된다.
5. 결과
다음 포스팅에서 화면단 구성에 대한 글을 작성.
'Back-End > Spring' 카테고리의 다른 글
JPA) @Entitygraph (0) | 2021.05.07 |
---|---|
Spring Data JPA 와 Postgresql 연동 (0) | 2021.04.21 |
ajax 통신시 URI로 이메일 문자열 보낼시 dot(.) 이하문자삭제 문제 (0) | 2021.03.25 |
MyBatis) MySQL로 Insert한 데이터의 자동생성된 pk 값 가져오기. (1) | 2021.03.22 |
MyBatis로 DB 칼럼 과 Java Property mapping 방법( XML, Annotation) (0) | 2021.03.22 |