-
Notifications
You must be signed in to change notification settings - Fork 0
/
[Chapter 8] 좋아요 구현하기
342 lines (254 loc) · 8.91 KB
/
[Chapter 8] 좋아요 구현하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
### 65. 좋아요 구현-Likes 모델 만들기
1. 좋아요 모델 만들기
story.js
```java
<button>
<i class="fas fa-heart active" id="storyLikeIcon-${image.id}" onclick="toggleLike(${image.id})"></i>
</button>
let likeIcon = $(`#storyLikeIcon-${imageId}`);
```
com.cos.photogramstart.domain.likes.Likes.java
```java
package com.cos.photogramstart.domain.likes;
import java.time.LocalDateTime;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import com.cos.photogramstart.domain.image.Image;
import com.cos.photogramstart.domain.subscribe.Subscribe;
import com.cos.photogramstart.domain.user.User;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@Entity // DB에 테이블을 생성
@Data
@NoArgsConstructor // 빈 생성자
@AllArgsConstructor // 전체 생성자
@Table(
uniqueConstraints = {
@UniqueConstraint(
name="likes_uk",
columnNames = {"imageId","userId"}
)
}
) // 유니크
public class Likes { // N
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // 번호 증가 전략이 데이터베이스를 따라간다.
private int id;
@JoinColumn(name="imageId")
@ManyToOne
private Image image; // 1
// 오류가 터지고 나서 잡아봅시다.
@JoinColumn(name="userId")
@ManyToOne
private User user; // 1
private LocalDateTime createDate;
@PrePersist // DB에 Insert가 되기 직전에 실행
public void createDate() {
this.createDate = LocalDateTime.now();
}
}
```
### 66. 좋아요 구현-좋아요 및 좋아요취소 API 구현하기
1. 좋아요 API 만들기
LikesRepository.java
```java
package com.cos.photogramstart.domain.likes;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
public interface LikesRepository extends JpaRepository<Likes, Integer>{
@Modifying
@Query(value="INSERT INTO likes(imageId, userId, createDate) VALUES(:imageId, :principalId, now()", nativeQuery = true)
int mLikes(int imageId, int principalId);
@Modifying
@Query(value="DELETE FROM likes WHERE imageId = :imageId AND userId = :principalId", nativeQuery = true)
int mUnLikes(int imageId, int principalId);
}
```
LikesService.java
```java
package com.cos.photogramstart.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.cos.photogramstart.domain.likes.LikesRepository;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class LikesService {
private final LikesRepository likesRepository;
@Transactional
public void 좋아요(int imageId, int principalId) {
likesRepository.mLikes(imageId, principalId);
}
@Transactional
public void 좋아요취소(int imageId, int principalId) {
likesRepository.mUnLikes(imageId, principalId);
}
}
```
ImageApiController.java
```java
package com.cos.photogramstart.web.api;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import com.cos.photogramstart.config.auth.PrincipalDatails;
import com.cos.photogramstart.domain.image.Image;
import com.cos.photogramstart.service.ImageService;
import com.cos.photogramstart.service.LikesService;
import com.cos.photogramstart.web.dto.CMRespDto;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@RestController
public class ImageApiController {
private final ImageService imageService;
private final LikesService likesService;
@GetMapping("/api/image")
public ResponseEntity<?> imageStory(@AuthenticationPrincipal PrincipalDatails principalDatails,
@PageableDefault(size=3) Pageable pageable){
Page<Image> images = imageService.이미지스토리(principalDatails.getUser().getId(), pageable);
return new ResponseEntity<>(new CMRespDto<>(1, "성공",images),HttpStatus.OK); // JS에서 호출해야함
}
@PostMapping("/api/image/{imageId}/likes")
public ResponseEntity<?> likes(@PathVariable int imageId, @AuthenticationPrincipal PrincipalDatails principalDatails){
likesService.좋아요(imageId,principalDatails.getUser().getId());
return new ResponseEntity<>(new CMRespDto<>(1,"좋아요성공",null),HttpStatus.CREATED);
}
@DeleteMapping("/api/image/{imageId}/likes")
public ResponseEntity<?> unlikes(@PathVariable int imageId, @AuthenticationPrincipal PrincipalDatails principalDatails){
likesService.좋아요취소(imageId,principalDatails.getUser().getId());
return new ResponseEntity<>(new CMRespDto<>(1,"좋아요취소성공",null),HttpStatus.OK);
}
}
```
### 67. 좋아요 구현-좋아요 뷰 렌더링
1. 좋아요 구현하기
ImageService.java
```java
private final ImageRepository imageRepository;
@Transactional(readOnly = true) // 영속성 컨텍스트 변경 감지를 해서, 더티체킹, flush(반영) X
public Page<Image> 이미지스토리(int principalId,Pageable pageable) {
Page<Image> images = imageRepository.mStory(principalId, pageable);
// 2(cos) 로그인
// images에 좋아요 상태 담기
images.forEach((image)->{
image.getLikes().forEach((like)->{
if(like.getUser().getId() == principalId) { // 해당 이미지에 좋아요한 사람들을 찾아서 현재 로그인한 사람이 좋아요 한것인지 비교
image.setLikeState(true);
}
});
});
return images;
}
```
story.js
```java
<div class="sl__item__contents">
<div class="sl__item__contents__icon">
<button>`;
if(image.likeState) {
item += `<i class="fas fa-heart active" id="storyLikeIcon-${image.id}" onclick="toggleLike(${image.id})"></i>`;
}else{
item += `<i class="fas fa-heart" id="storyLikeIcon-${image.id}" onclick="toggleLike(${image.id})"></i>`;
}
item += `
</button>
</div>
```
### 68. 좋아요 구현-좋아요 카운트 뷰 렌더링
Image.java
```java
@Transient
private int likeCount;
```
story.js
```java
<span class="like"><b id="storyLikeCount-${image.id}">${image.likeCount}</b>likes</span>
```
### 69. 좋아요 구현-좋아요 구현 완료
ImageService.java
```java
private final ImageRepository imageRepository;
@Transactional(readOnly = true) // 영속성 컨텍스트 변경 감지를 해서, 더티체킹, flush(반영) X
public Page<Image> 이미지스토리(int principalId,Pageable pageable) {
Page<Image> images = imageRepository.mStory(principalId, pageable);
// 2(cos) 로그인
// images에 좋아요 상태 담기
images.forEach((image)->{
image.setLikeCount(image.getLikes().size());
image.getLikes().forEach((like)->{
if(like.getUser().getId() == principalId) { // 해당 이미지에 좋아요한 사람들을 찾아서 현재 로그인한 사람이 좋아요 한것인지 비교
image.setLikeState(true);
}
});
});
return images;
}
```
story.js
```java
// (3) 좋아요, 안좋아요
function toggleLike(imageId) {
let likeIcon = $(`#storyLikeIcon-${imageId}`);
if (likeIcon.hasClass("far")) { // 좋아요 하겠다
$.ajax({
type:"post",
url:`/api/image/${imageId}/likes`,
dataType:"json"
}).done(res=>{
let likeCountStr = $(`#storyLikeCount-${imageId}`).text();
let likeCount = Number(likeCountStr)+1;
$(`#storyLikeCount-${imageId}`).text(likeCount);
likeIcon.addClass("fas");
likeIcon.addClass("active");
likeIcon.removeClass("far");
}).fail(error=>{
console.log("오류",error);
});
} else { // 좋아요 취소 하겠다.
$.ajax({
type:"delete",
url:`/api/image/${imageId}/likes`,
dataType:"json"
}).done(res=>{
let likeCountStr = $(`#storyLikeCount-${imageId}`).text();
let likeCount = Number(likeCountStr)-1;
$(`#storyLikeCount-${imageId}`).text(likeCount);
likeIcon.removeClass("fas");
likeIcon.removeClass("active");
likeIcon.addClass("far");
}).fail(error=>{
console.log("오류",error);
});
}
}
```
### 70. 좋아요 구현-무한참조 버그 잡기
Likes.java
```java
// 오류가 터지고 나서 잡아봅시다.
@JsonIgnoreProperties({"images"})
@JoinColumn(name="userId")
@ManyToOne
private User user; // 1
```