๐๋ฆฌ๋ทฐ ํ์ด์ง ์์ฑ
pastOrderList.forEach(order => {
let menuList = order.menus.map(menu => menu.menuname).join('<br>');
$('#my-order-info').append(`
<div class="past-order-pack">
<div class="past-order">
<div>๊ฐ๊ฒ ์ด๋ฆ: ${order.store}</div>
<div>์๋ น์ธ ์ด๋ฆ: ${order.receiverName}</div>
<div>๋ฉ๋ด
<div>${menuList}</div>
</div>
</div>
<button type="button" class="btn btn-secondary" id="review-submit" onclick="createReview()">
๋ฆฌ๋ทฐ ๋จ๊ธฐ๊ธฐ
</button>
</div>
`);
});
๋ฆฌ๋ทฐ์ orderId๊ฐ ํ์ํ๋๊น ๊ทธ๊ฑฐ ๋ฌ์์ค
@Builder
public record OrderResponse (
Long id,
String store,
String receiverName,
List<MenuResponse> menus
){
}
<button type="button" class="btn btn-secondary" id="review-submit" order = "${order.id} onclick="createReview(this)">
function createReview(button){
var orderId = $(button).attr("order");
const host = 'http://' + window.location.host;
window.location.replace(host + `/menu/${postId}`);
}
orderId ๋ฃ์ด์ค๋ค์์ ํด๋ฆญ์์ ReviewFrontController ํธ์ถํจ
@Controller
@RequiredArgsConstructor
public class ReviewFrontController {
@GetMapping("/createreview/{orderId}")
public String createReviewPage(
@PathVariable(name = "orderId") Long orderId,
Model model
) {
model.addAttribute("orderId", orderId);
return "domain/review/createreview";
}
}
createreview.html ๋ง๋ฆ
https://getbootstrap.kr/docs/5.0/forms/checks-radios/
์ฒดํฌ๋ฐ์ค์ ๋ผ๋์ค๋ฒํผ
์์ ํ ์๋ก์ ์ง ์ฒดํฌ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํด ํฌ๋ก์ค ๋ธ๋ผ์ฐ์ ์ ํฌ๋ก์ค ๊ธฐ๊ธฐ๋ก ์ผ๊ด๋ ์ฒดํฌ ๋ฐ์ค์ ๋ผ๋์ค ๋ฒํผ์ ๋ง๋ค ์ ์์ต๋๋ค.
getbootstrap.kr
<input type="checkbox" class="btn-check" id="btn-check-outlined" autocomplete="off">
<label class="btn btn-outline-primary" for="btn-check-outlined">Single toggle</label><br>
์ด๊ฑฐ ์จ๋ด
ReviewEnum ์์ฑํจ
@Getter
@RequiredArgsConstructor
public enum ReviewEnum {
//positive
GOODMANNER("์น์ ํ๊ณ ๋งค๋๊ฐ ์ข์์"),
GOODTIME("์๊ฐ ์ฝ์์ ์ ์ง์ผ์"),
GOODCOMM("์ํต๊ณผ ์๋ต์ด ๋นจ๋ผ์"),
//negative
BADTIME("์ฝ์ ์๊ฐ์ ๋ํ๋์ง ์์์ด์"),
NOSHOW("์์ ๋ํ๋์ง ์์์ด์"),
NOMONEY("๊ฐ์ ์ง๋ถํ์ง ์์์ด์"),
BADCOMM("์ํต๊ณผ ์๋ต์ด ๋๋ ค์"),
BADMANNER("๋ถ์น์ ํ๊ณ ๋งค๋๊ฐ ์ข์ง ์์์");
private final String comment;
}
<div style="padding:10px;width:1000px; margin:auto; height:max-content">
<div>
<input type="checkbox" class="btn-check" id="goodmanner" autocomplete="off">
<label class="btn btn-outline-primary" onclick="goodmanner()" for="goodmanner">์น์ ํ๊ณ ๋งค๋๊ฐ ์ข์์</label><br>
<input type="checkbox" class="btn-check" id="goodtime" autocomplete="off">
<label class="btn btn-outline-primary" onclick="goodtime()" for="goodtime">์๊ฐ ์ฝ์์ ์ ์ง์ผ์</label><br>
<input type="checkbox" class="btn-check" id="goodcomm" autocomplete="off">
<label class="btn btn-outline-primary" onclick="goodcomm()" for="goodcomm">์ํต๊ณผ ์๋ต์ด ๋นจ๋ผ์</label><br>
<input type="checkbox" class="btn-check" id="badtime" autocomplete="off">
<label class="btn btn-outline-primary" onclick="badtime()" for="badtime">์ฝ์ ์๊ฐ์ ๋ํ๋์ง ์์์ด์</label><br>
<input type="checkbox" class="btn-check" id="noshow" autocomplete="off">
<label class="btn btn-outline-primary" onclick="noshow()" for="noshow">์์ ๋ํ๋์ง ์์์ด์</label><br>
<input type="checkbox" class="btn-check" id="nomoney" autocomplete="off">
<label class="btn btn-outline-primary" onclick="nomoney()" for="nomoney">๊ฐ์ ์ง๋ถํ์ง ์์์ด์</label><br>
<input type="checkbox" class="btn-check" id="badcomm" autocomplete="off">
<label class="btn btn-outline-primary" onclick="badcomm()" for="badcomm">์ํต๊ณผ ์๋ต์ด ๋๋ ค์</label><br>
<input type="checkbox" class="btn-check" id="badmanner" autocomplete="off">
<label class="btn btn-outline-primary" onclick="badmanner()" for="badmanner">๋ถ์น์ ํ๊ณ ๋งค๋๊ฐ ์ข์ง ์์์</label><br>
<button class="btn btn-outline-secondary" menuId=${menu.id} onclick="sendReview()"
type="button">๋ฆฌ๋ทฐ ์ ์ก
</button>
</div>
<div id="dropaddmenu">
</div>
</div>
function createReview(button){
var orderId = $(button).attr("order");
const host = 'http://' + window.location.host;
window.location.replace(host + `/review/${orderId}`);
}
<button id = "${post.id}" post = "${post.id}" class="btn btn-outline-secondary" onclick="sendData(this)" type="button">์กฐํ</button>
</div>
pastOrderList.forEach(order => {
let menuList = order.menus.map(menu => menu.menuname).join('<br>');
$('#my-order-info').append(`
<div class="past-order-pack">
<div class="past-order">
<div>๊ฐ๊ฒ ์ด๋ฆ: ${order.store}</div>
<div>์๋ น์ธ ์ด๋ฆ: ${order.receiverName}</div>
<div>๋ฉ๋ด
<div>${menuList}</div>
</div>
</div>
<button type="button" class="btn btn-secondary" id="review-submit" order = "${order.id} onclick="createReview(this)">
๋ฆฌ๋ทฐ ๋จ๊ธฐ๊ธฐ
</button>
</div>
`);
});
})
}
function createReview(button){
var orderId = $(button).attr("order");
const host = 'http://' + window.location.host;
window.location.replace(host + `/review/${orderId}`);
}
์ ๋ฒํผ์ด ์ ๋๋ฆฌ์ง
function createReview(button){
var orderId = $(button).attr("order");
const host = 'http://' + window.location.host;
window.location.replace(host + `/createreview/${orderId}`);
}
<button type="button" class="btn btn-secondary" id="review-submit" order = "${order.id} onclick="createReview(this)">
๐ฉ๋ฌธ์ : createReview๊ฐ ์ ๋ถ๋ฆฌ๋๋ฏ? ์ธ์์ด ์ ๋จ
์ no usage๋ก ๋จ์ง?
โณ ๋ฌธ์ ํด๊ฒฐ : onclick ์ฌ์ด์ "๋ฅผ ์ถ๊ฐํด์ค
<button type="button" class="btn btn-secondary" id="review-submit" order="${order.id}" onclick="createReview(this)">
๋ฆฌ๋ทฐ ๋จ๊ธฐ๊ธฐ
</button>
์คํ ์์ ํ๋๊น ๋๋ค!
ํ์ด์ง๊ฐ ์ ๋ฌ๋ค
<div style="padding:10px;width:1000px; margin:auto; height:max-content">
<div>
<input type="checkbox" class="btn-check" id="goodmanner" autocomplete="off">
<label class="btn btn-outline-primary" onclick="goodmanner(this)" for="goodmanner">์น์ ํ๊ณ ๋งค๋๊ฐ ์ข์์</label><br>
<input type="checkbox" class="btn-check" id="goodtime" autocomplete="off">
<label class="btn btn-outline-primary" onclick="goodtime(this)" for="goodtime">์๊ฐ ์ฝ์์ ์ ์ง์ผ์</label><br>
<input type="checkbox" class="btn-check" id="goodcomm" autocomplete="off">
<label class="btn btn-outline-primary" onclick="goodcomm(this)" for="goodcomm">์ํต๊ณผ ์๋ต์ด ๋นจ๋ผ์</label><br>
<input type="checkbox" class="btn-check" id="badtime" autocomplete="off">
<label class="btn btn-outline-primary" onclick="badtime(this)" for="badtime">์ฝ์ ์๊ฐ์ ๋ํ๋์ง ์์์ด์</label><br>
<input type="checkbox" class="btn-check" id="noshow" autocomplete="off">
<label class="btn btn-outline-primary" onclick="noshow(this)" for="noshow">์์ ๋ํ๋์ง ์์์ด์</label><br>
<input type="checkbox" class="btn-check" id="nomoney" autocomplete="off">
<label class="btn btn-outline-primary" onclick="nomoney(this)" for="nomoney">๊ฐ์ ์ง๋ถํ์ง ์์์ด์</label><br>
<input type="checkbox" class="btn-check" id="badcomm" autocomplete="off">
<label class="btn btn-outline-primary" onclick="badcomm(this)" for="badcomm">์ํต๊ณผ ์๋ต์ด ๋๋ ค์</label><br>
<input type="checkbox" class="btn-check" id="badmanner" autocomplete="off">
<label class="btn btn-outline-primary" onclick="badmanner(this)" for="badmanner">๋ถ์น์ ํ๊ณ ๋งค๋๊ฐ ์ข์ง ์์์</label><br>
<button class="btn btn-outline-secondary" menuId=${menu.id} onclick="sendReview()"
type="button">๋ฆฌ๋ทฐ ์ ์ก
</button>
</div>
<div id="dropaddmenu">
</div>
</div>
์ด๋ ๊ฒ ํ๊ณ
var goodmanner;
var goodtime;
var goodcomm;
var badtime;
var noshow;
var nomoney;
var badcomm;
var badmanner;
function goodmanner(label){
var checkbox = $('#' + $(label).attr('for'));
if (checkbox.prop('checked')) {
goodmanner = true;
} else {
goodmanner = false;
}
}
function goodtime(label){
var checkbox = $('#' + $(label).attr('for'));
if (checkbox.prop('checked')) {
goodtime = true;
} else {
goodtime = false;
}
}
function goodcomm(label){
var checkbox = $('#' + $(label).attr('for'));
if (checkbox.prop('checked')) {
goodcomm = true;
} else {
goodcomm = false;
}
}
function badtime(label){
var checkbox = $('#' + $(label).attr('for'));
if (checkbox.prop('checked')) {
badtime = true;
} else {
badtime = false;
}
}
function noshow(label){
var checkbox = $('#' + $(label).attr('for'));
if (checkbox.prop('checked')) {
noshow = true;
} else {
noshow = false;
}
}
function nomoney(label){
var checkbox = $('#' + $(label).attr('for'));
if (checkbox.prop('checked')) {
nomoney = true;
} else {
nomoney = false;
}
}
function badcomm(label){
var checkbox = $('#' + $(label).attr('for'));
if (checkbox.prop('checked')) {
badcomm = true;
} else {
badcomm = false;
}
}
function badmanner(label) {
var checkbox = $('#' + $(label).attr('for'));
if (checkbox.prop('checked')) {
badmanner = true;
} else {
badmanner = false;
}
}
ํจ์ ๋ฌ์๋ด
์ด์ ๋ฆฌ๋ทฐ๋ฅผ ๋ณด๋ด๋ด
// ๋ฆฌ๋ทฐ ์์ฑ
@PostMapping("")
public ApiResponse<Void> review(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@RequestBody ReviewRequest reviewReq
) {
reviewService.review(reviewReq, userDetails.getUser());
return new ApiResponse<>(HttpStatus.OK.value(), "๋ฆฌ๋ทฐ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ์ ์กํ์ต๋๋ค.");
}
์ฌ๊ธฐ๋ก ๋ณด๋ด๋ฉด ๋จ
public record ReviewRequest(
Long orderId,
ScoreEnum score,
boolean goodmanner,
boolean goodtime,
boolean goodcomm,
boolean badtime,
boolean noshow,
boolean nomoney,
boolean badcomm,
boolean badmanner
) {
}
var checkbox = $('#' + $(label).attr('for'));
->
var checkbox = $(label).attr('for');
var goodmanner;
var goodtime;
var goodcomm;
var badtime;
var noshow;
var nomoney;
var badcomm;
var badmanner;
function goodmanner(label) {
var checkbox = $(label).attr('for');
if (checkbox.prop('checked')) {
goodmanner = true;
} else {
goodmanner = false;
}
}
function goodtime(label) {
var checkbox = $(label).attr('for');
if (checkbox.prop('checked')) {
goodtime = true;
} else {
goodtime = false;
}
}
function goodcomm(label) {
var checkbox = $(label).attr('for');
if (checkbox.prop('checked')) {
goodcomm = true;
} else {
goodcomm = false;
}
}
function badtime(label) {
var checkbox = $(label).attr('for');
if (checkbox.prop('checked')) {
badtime = true;
} else {
badtime = false;
}
}
function noshow(label) {
var checkbox = $(label).attr('for');
if (checkbox.prop('checked')) {
noshow = true;
} else {
noshow = false;
}
}
function nomoney(label) {
var checkbox = $(label).attr('for');
if (checkbox.prop('checked')) {
nomoney = true;
} else {
nomoney = false;
}
}
function badcomm(label) {
var checkbox = $(label).attr('for');
if (checkbox.prop('checked')) {
badcomm = true;
} else {
badcomm = false;
}
}
function badmanner(label) {
var checkbox = $(label).attr('for');
if (checkbox.prop('checked')) {
badmanner = true;
} else {
badmanner = false;
}
}
→
์ var๋ค์ sendReview/sendData์์ ์ธ์์ ๋ชปํด์ ์์ชฝ์ผ๋ก ์ฎ๊น
function sendReview() {
let score = $('#customRange').val();
var goodmanner;
var goodmannerbox = $(goodmanner).attr('for');
if (goodmannerbox.prop('checked')) {
goodmanner = true;
}
sendData(score,goodmanner,goodtime,goodcomm,badtime,noshow,nomoney,badcomm,badmanner);
}
function sendData(score,goodmanner,goodtime,goodcomm,badtime,noshow,nomoney,badcomm,badmanner){
let idForOrder = [[${orderId}]];
$.ajax({
type: 'POST',
url: `/api/v1/reviews`,
dataType: "json",
contentType: 'application/json',
data: JSON.stringify({
orderId: idForOrder,
score: score,
goodmanner: goodmanner,
goodtime: goodtime,
goodcomm: goodcomm,
badtime: badtime,
noshow: noshow,
nomoney: nomoney,
badcomm: badcomm,
badmanner: badmanner
}),
success: function (response) {
console.log('Success:', response);
alert(response.message);
},
error: function (error) {
console.error('Error:', error);
}
});
}
๐ฉ ๋ฌธ์ : ์ฒดํฌ๋ฅผ
2:106 Uncaught TypeError: checkbox.prop is not a function at badtime (2:106:18) at HTMLLabelElement.onclick (2:41:82) badtime @ 2:106 onclick @ 2:41 2:159 Uncaught TypeError: Cannot read properties of undefined (reading 'prop') at sendReview (2:159:23) at HTMLButtonElement.onclick (2:55:27)
let score = $('#customRange').val();
var goodmannerbox = $('#goodmanner');
var goodmanner = goodmannerbox.checked;
์ด๋ ๊ฒ ํด๋ด
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type boolean from Object value (token JsonToken.START_OBJECT)]
public record ReviewRequest(
Long orderId,
ScoreEnum score,
boolean goodmanner,
boolean goodtime,
boolean goodcomm,
boolean badtime,
boolean noshow,
boolean nomoney,
boolean badcomm,
boolean badmanner
) {
}
Boolean์ผ๋ก ๋ฐ๊ฟ๋ด
2024-01-19T15:40:21.105+09:00 WARN 13976 --- [nio-8080-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type java.lang.Boolean from Object value (token JsonToken.START_OBJECT)]
๋ ๋๊ฐ์๊ฑฐ ๋ธ
2024-01-19T15:41:10.396+09:00 WARN 13976 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type java.lang.Boolean from Object value (token JsonToken.START_OBJECT)]
var goodmanner;
var goodmannerbox = $('#goodmanner');
if(goodmannerbox.checked){
goodmanner = true;
}
์ด๋ ๊ฒ ๋ฐ๊ฟ๋ด
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type java.lang.Boolean from Object value (token JsonToken.START_OBJECT)]
function sendReview() {
let score = $('#customRange').val();
var goodmanner;
var goodmannerbox = $('#goodmanner');
if(goodmannerbox.checked){
goodmanner = "true";
}
์ด๋ ๊ฒ ๋ฐ๊ฟ๋ด
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type java.lang.Boolean from Object value (token JsonToken.START_OBJECT)]
public record ReviewRequest(
Long orderId,
ScoreEnum score,
boolean goodmanner,
boolean goodtime,
boolean goodcomm,
boolean badtime,
boolean noshow,
boolean nomoney,
boolean badcomm,
boolean badmanner
) {
}
๋ค์๋ฐ๊ฟ๋ด
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type boolean from Object value (token JsonToken.START_OBJECT)]
if(goodmannerbox.checked){
goodmanner = true;
}else{
goodmanner = false;
}
์ด๋ ๊ฒ ํด๋ด
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type boolean from Object value (token JsonToken.START_OBJECT)]
์๊ด์์์
์ ์ ์๋ ๊น ํ๊ณ ์ฝ๋๋ณด๋ฉด์ ๋ฉ๋๋ฆฌ๋๋ฐ ๊นจ๋ฌ์์ด ์ฐพ์์ด
package com.moayo.moayoeats.backend.domain.review.dto.request;
import com.moayo.moayoeats.backend.domain.score.entity.ScoreEnum;
public record ReviewRequest(
Long orderId,
ScoreEnum score,
boolean goodmanner,
boolean goodtime,
boolean goodcomm,
boolean badtime,
boolean noshow,
boolean nomoney,
boolean badcomm,
boolean badmanner
) {
}
๋ฌธ์ ๋ ๋ค๋ฅธ๋ฐ ์์๋ค!!!
jsํ์ผ ๋ฐฑ๋ ๋ค์ฌ๋ค๋ด๋ ์๋จ
์๋๋ฉด ๋ฌธ์ ๋ Spring์ธก์ ์์ผ๋๊น
Getter๋ Setter๋ Constructor๋ ์๋ฌ๋ ค์๋๋ฐ ์ด๋ป๊ฒ ๋ณํ์ํค๋๊ณ ์ ใ ใ ใ
package com.moayo.moayoeats.backend.domain.review.dto.request;
import com.moayo.moayoeats.backend.domain.score.entity.ScoreEnum;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public record ReviewRequest(
Long orderId,
ScoreEnum score,
boolean goodmanner,
boolean goodtime,
boolean goodcomm,
boolean badtime,
boolean noshow,
boolean nomoney,
boolean badcomm,
boolean badmanner
) {
}
๋๊ฐ ๋ฌ์์คฌ๋ค
์๋ ์๋ฌ๋จ
๋ ์ฝ๋๋ ์ ์ด์ ํฌํจ์ด์ผ
You expect a boolean from your @RequestBody Boolean vote however JSON sends text. You can either use the Payload class as suggested already but you can also simply change your controller to expect a String like this @RequestBody String vote and convert that string into boolean using Boolean.valueOf(vote) to be able to use it where you need it.
๋ผ๊ณ ํ๊ธธ๋ ๊ท์ฐฎ์์ int๋ก ๋ฐ๊ฟ์ฃผ๊ณ 0์ด๋ 1๋ฃ์
public record ReviewRequest(
Long orderId,
ScoreEnum score,
Integer goodmanner,
Integer goodtime,
Integer goodcomm,
Integer badtime,
Integer noshow,
Integer nomoney,
Integer badcomm,
Integer badmanner
) {
}
var goodmanner;
var goodmannerbox = $('#goodmanner');
if(goodmannerbox.checked){
goodmanner = 1;
}else{
goodmanner = 0;
}
์ ๊ทผ๋ฐ ๋ด๊ฐ ๊ท์ฐฎ์์ goodmanner๊น์ง๋ง ํด๋์ ๊ทธ๋ฌ๋๊ฑฐ๋ฉด ์ด๋กํ์ง ใ ใ
sendData(score,goodmanner,goodtime,goodcomm,badtime,noshow,nomoney,badcomm,badmanner);
์ ๋ ธ๊ฐ๋คํ๋๋ ์ด์ ๋จ
function sendReview() {
let score = $('#customRange').val();
var goodmanner;
var goodmannerbox = $('#goodmanner');
if(goodmannerbox.checked){
goodmanner = 1;
}else{
goodmanner = 0;
}
var goodtime;
var goodtimebox = $('#goodtime');
if(goodtimebox.checked){
goodtime = 1;
}else{
goodtime = 0;
}
var goodcomm;
var goodcommbox = $('#goodcomm');
if(goodcommbox.checked){
goodcomm = 1;
}else{
goodcomm = 0;
}
var badtime;
var badtimebox = $('#goodcomm');
if(badtimebox.checked){
badtime = 1;
}else{
badtime = 0;
}
var noshow;
var noshowbox = $('#goodcomm');
if(noshowbox.checked){
noshow = 1;
}else{
noshow = 0;
}
var nomoney;
var nomoneybox = $('#goodcomm');
if(nomoneybox.checked){
nomoney = 1;
}else{
nomoney = 0;
}
var badcomm;
var badcommbox = $('#goodcomm');
if(badcommbox.checked){
badcomm = 1;
}else{
badcomm = 0;
}
var badmanner;
var badmannerbox = $('#goodcomm');
if(badmannerbox.checked){
badmanner = 1;
}else{
badmanner = 0;
}
sendData(score,goodmanner,goodtime,goodcomm,badtime,noshow,nomoney,badcomm,badmanner);
}
function sendData(score,goodmanner,goodtime,goodcomm,badtime,noshow,nomoney,badcomm,badmanner){
let idForOrder = [[${orderId}]];
$.ajax({
type: 'POST',
url: `/api/v1/reviews`,
dataType: "json",
contentType: 'application/json',
data: JSON.stringify({
orderId: idForOrder,
score: score,
goodmanner: goodmanner,
goodtime: goodtime,
goodcomm: goodcomm,
badtime: badtime,
noshow: noshow,
nomoney: nomoney,
badcomm: badcomm,
badmanner: badmanner
}),
success: function (response) {
console.log('Success:', response);
alert(response.message);
},
error: function (error) {
console.error('Error:', error);
}
});
}
๊ทผ๋ฐ ๋ด๊ฐ ๋ฐ๋ณด์งํ๋ค๋ ์๊ฐ์ ์ง์ธ์๊ฐ ์์
๊ฐ์ ์ ๋ณด๋ด๋๊ณ ๊ทธ๋ฅ goodtime:goodtime์ผ๋ก ์ง์ ํ๊ณ ์์ด์ ๊ทธ๋ฌ๋๊ฑฐ๊ฐ์๋ฐ…
๋ค์ ๋๋๋ฆฌ๊ณ ํด๋ด
public record ReviewRequest(
Long orderId,
ScoreEnum score,
Integer goodmanner,
Integer goodtime,
Integer goodcomm,
Integer badtime,
Integer noshow,
Integer nomoney,
Integer badcomm,
Integer badmanner
) {
}
→
package com.moayo.moayoeats.backend.domain.review.dto.request;
import com.moayo.moayoeats.backend.domain.score.entity.ScoreEnum;
public record ReviewRequest(
Long orderId,
ScoreEnum score,
boolean goodmanner,
boolean goodtime,
boolean goodcomm,
boolean badtime,
boolean noshow,
boolean nomoney,
boolean badcomm,
boolean badmanner
) {
}
private void updateReview(ReviewRequest reviewReq, User receiver) {
List<Review> reviews = reviewRepository.findAllByUser(receiver);
List<Review> updated = new ArrayList<>();
if (reviewReq.goodmanner()==1) {
Review review = findReviewByContent(reviews, ReviewEnum.GOODMANNER, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.goodcomm()==1) {
Review review = findReviewByContent(reviews, ReviewEnum.GOODCOMM, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.goodtime()==1) {
Review review = findReviewByContent(reviews, ReviewEnum.GOODTIME, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.badtime()==1) {
Review review = findReviewByContent(reviews, ReviewEnum.BADTIME, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.noshow()==1) {
Review review = findReviewByContent(reviews, ReviewEnum.NOSHOW, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.nomoney()==1) {
Review review = findReviewByContent(reviews, ReviewEnum.NOMONEY, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.badcomm()==1) {
Review review = findReviewByContent(reviews, ReviewEnum.BADCOMM, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.badmanner()==1) {
Review review = findReviewByContent(reviews, ReviewEnum.BADMANNER, receiver);
review.increaseCount();
updated.add(review);
}
reviewRepository.saveAll(updated);
}
→
private void updateReview(ReviewRequest reviewReq, User receiver) {
List<Review> reviews = reviewRepository.findAllByUser(receiver);
List<Review> updated = new ArrayList<>();
if (reviewReq.goodmanner()) {
Review review = findReviewByContent(reviews, ReviewEnum.GOODMANNER, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.goodcomm()) {
Review review = findReviewByContent(reviews, ReviewEnum.GOODCOMM, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.goodtime()) {
Review review = findReviewByContent(reviews, ReviewEnum.GOODTIME, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.badtime()) {
Review review = findReviewByContent(reviews, ReviewEnum.BADTIME, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.noshow()) {
Review review = findReviewByContent(reviews, ReviewEnum.NOSHOW, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.nomoney()) {
Review review = findReviewByContent(reviews, ReviewEnum.NOMONEY, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.badcomm()) {
Review review = findReviewByContent(reviews, ReviewEnum.BADCOMM, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.badmanner()) {
Review review = findReviewByContent(reviews, ReviewEnum.BADMANNER, receiver);
review.increaseCount();
updated.add(review);
}
reviewRepository.saveAll(updated);
}
์๋ก์ด ๋ฌธ์ ๋ฐ์
์ ์ ํจ์๋ค ๋ค ์ง์ฐ๊ณ onclick์์๋ ์ ์ง์์ ์ ๋ฌ๋๊ฑฐ์
์ ์ก์ ๋๋๋ฐ ๋ญ๊ฐ๋๊ฑฐ์ง
์ ๋ค์ด์จ ๊ฑฐ ๊ฐ์๋ฐ
14๋ฒ Order์ ๋ํด์ ๋ฆฌ๋ทฐ๋ฅผ ์จ๋ด
Order๋ ์ญ์ ๋๋๋ฐ
Review๋ ์ ์๊น
@Override
public void review(ReviewRequest reviewReq, User user) {
Order order = findOrderById(reviewReq.orderId());
//check if the receiver exists, to clarify that the review hasn't been made, not because the user doesn't exist
if (!order.getUser().getId().equals(user.getId())) {
throw new GlobalException(OrderErrorCode.FORBIDDEN_ACCESS);
}
User receiver = order.getReceiver();
checkIfUserExists(receiver.getId());
updateScore(reviewReq.score(), receiver);
updateReview(reviewReq, receiver);
orderRepository.delete(order);
}
private void updateReview(ReviewRequest reviewReq, User receiver) {
List<Review> reviews = reviewRepository.findAllByUser(receiver);
List<Review> updated = new ArrayList<>();
if (reviewReq.goodmanner()) {
Review review = findReviewByContent(reviews, ReviewEnum.GOODMANNER, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.goodcomm()) {
Review review = findReviewByContent(reviews, ReviewEnum.GOODCOMM, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.goodtime()) {
Review review = findReviewByContent(reviews, ReviewEnum.GOODTIME, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.badtime()) {
Review review = findReviewByContent(reviews, ReviewEnum.BADTIME, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.noshow()) {
Review review = findReviewByContent(reviews, ReviewEnum.NOSHOW, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.nomoney()) {
Review review = findReviewByContent(reviews, ReviewEnum.NOMONEY, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.badcomm()) {
Review review = findReviewByContent(reviews, ReviewEnum.BADCOMM, receiver);
review.increaseCount();
updated.add(review);
}
if (reviewReq.badmanner()) {
Review review = findReviewByContent(reviews, ReviewEnum.BADMANNER, receiver);
review.increaseCount();
updated.add(review);
}
reviewRepository.saveAll(updated);
}
Score๋ ๋ค์ด๊ฐ๋์ง ๋ชจ๋ฅด๊ฒ ์
private Review findReviewByContent(List<Review> reviews, ReviewEnum content, User user) {
//get Review when exists, make a new one when not
for (Review review : reviews) {
if (review.getContent().equals(content)) {
reviews.remove(review);
return review;
}
}
return new Review(user, content);
}
์์ผ๋ฉด ๋ง๋ค์ด์ผ ๋๋๋ฐ
์ ๊ฒ ๋ค ๋์์ด ์ ํ๋๊ฑด
if (reviewReq.goodmanner()) {
์ด๊ฑฐ๋ถํฐ ๋์์ ์ ํ๊ธฐ ๋๋ฌธ์ธ๊ฑฐ ๊ฐ์
ํฌ์คํธ๋งจ์ผ๋ก ํ ์คํธํด๋ด
16๋ฒ์ผ๋ก ํ ์คํธํด๋ด
{
"orderId" : 16,
"score" : "FIVE",
"goodmanner" : true,
"goodtime" : true
}
{
"status": 200,
"message": "๋ฆฌ๋ทฐ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ์ ์กํ์ต๋๋ค.",
"data": null
}
์ผ๋จ Order๋ ์ฑ๊ณต์ ์ผ๋ก ์ฌ๋ผ์ง๊ณ
Review๋ ์๊นใ ใ
boolean๋์ ๊ทธ๋ฅ int๋ฅผ ๋ณด๋ด๋ด
package com.moayo.moayoeats.backend.domain.review.dto.request;
import com.moayo.moayoeats.backend.domain.score.entity.ScoreEnum;
public record ReviewRequest(
Long orderId,
ScoreEnum score,
Integer goodmanner,
Integer goodtime,
Integer goodcomm,
Integer badtime,
Integer noshow,
Integer nomoney,
Integer badcomm,
Integer badmanner
) {
}
function sendReview() {
let score = $('#customRange').val();
let scoreEnum;
if(score === 1){
scoreEnum = "ONE"
}else if(score === 2){
scoreEnum = "TWO"
}else if(score === 3){
scoreEnum = "THREE"
}else if(score === 4){
scoreEnum = "FOUR"
}else if(score === 5){
scoreEnum = "FIVE"
}
var goodmanner;
var goodmannerbox = $('#goodmanner');
if(goodmannerbox.checked){
goodmanner = 1;
}else{
goodmanner = 0;
}
var goodtime;
var goodtimebox = $('#goodtime');
if(goodtimebox.checked){
goodtime = 1;
}else{
goodtime = 0;
}
var goodcomm;
var goodcommbox = $('#goodcomm');
if(goodcommbox.checked){
goodcomm = 1;
}else{
goodcomm = 0;
}
var badtime;
var badtimebox = $('#goodcomm');
if(badtimebox.checked){
badtime = 1;
}else{
badtime = 0;
}
var noshow;
var noshowbox = $('#goodcomm');
if(noshowbox.checked){
noshow = 1;
}else{
noshow = 0;
}
var nomoney;
var nomoneybox = $('#goodcomm');
if(nomoneybox.checked){
nomoney = 1;
}else{
nomoney = 0;
}
var badcomm;
var badcommbox = $('#goodcomm');
if(badcommbox.checked){
badcomm = 1;
}else{
badcomm = 0;
}
var badmanner;
var badmannerbox = $('#goodcomm');
if(badmannerbox.checked){
badmanner = 1;
}else{
badmanner = 0;
}
sendData(scoreEnum,goodmanner,goodtime,goodcomm,badtime,noshow,nomoney,badcomm,badmanner);
}
function sendData(score,goodmanner,goodtime,goodcomm,badtime,noshow,nomoney,badcomm,badmanner){
let idForOrder = [[${orderId}]];
$.ajax({
type: 'POST',
url: `/api/v1/reviews`,
dataType: "json",
contentType: 'application/json',
data: JSON.stringify({
orderId: idForOrder,
score: score,
goodmanner: goodmanner,
goodtime: goodtime,
goodcomm: goodcomm,
badtime: badtime,
noshow: noshow,
nomoney: nomoney,
badcomm: badcomm,
badmanner: badmanner
}),
success: function (response) {
console.log('Success:', response);
alert(response.message);
},
error: function (error) {
console.error('Error:', error);
}
});
}
๋ค์ ํด๋ด
2024-01-19T16:26:57.703+09:00 ERROR 32796 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.NullPointerException: Cannot invoke "com.moayo.moayoeats.backend.domain.score.entity.ScoreEnum.getScore()" because "score" is null] with root cause
java.lang.NullPointerException: Cannot invoke "com.moayo.moayoeats.backend.domain.score.entity.ScoreEnum.getScore()" because "score" is null
at com.moayo.moayoeats.backend.domain.score.entity.Score.update(Score.java:44) ~[main/:na]
private void updateScore(ScoreEnum scoreEnum, User user) {
Score score = findScoreByUser(user);
score.update(scoreEnum);//->์ด๊ฒ ์๋ฌ๊ฐ ๋จ
scoreRepository.save(score);
}
private Score findScoreByUser(User user) {
return scoreRepository.findByUser(user).orElse(new Score(user));
}
public void update(ScoreEnum score){
this.total += score.getScore();
this.count++;
}
ScoreEnum์ด ์ null์ธ๋ฐ์
์ 0์ด๋ผ ์ ๋ฃ์ด์คฌ๋๋ด
let score = $('#customRange').val();
let scoreEnum;
if(score === 1){
scoreEnum = "ONE"
}else if(score === 2){
scoreEnum = "TWO"
}else if(score === 3){
scoreEnum = "THREE"
}else if(score === 4){
scoreEnum = "FOUR"
}else if(score === 5){
scoreEnum = "FIVE"
}
<label for="customRange" class="form-label">์ ์</label>
<input type="range" class="form-range" min="0" max="5" id="customRange">
min 0 → min 1
๋ค์ ํด๋ด
java.lang.NullPointerException: Cannot invoke "com.moayo.moayoeats.backend.domain.score.entity.ScoreEnum.getScore()" because "score" is null at com.moayo.moayoeats.backend.domain.score.entity.Score.update(Score.java:44) ~[main/:na]
๋ ์ด๋ฌ๋ค
let score = $('#customRange').val();
let scoreEnum;
if(score === 1){
scoreEnum = "ONE"
}else if(score === 2){
scoreEnum = "TWO"
}else if(score === 3){
scoreEnum = "THREE"
}else if(score === 4){
scoreEnum = "FOUR"
}else if(score === 5){
scoreEnum = "FIVE"
}
์ด๊ฒ ์ ๋๋ ๊ฑฐ ๊ฐ์
// Convert the string to a number
score = parseInt(score, 10);
์ด๋ ๊ฒ ํด์ฃผ๋๊ฐ
์๋๋ฉด ๊ทธ๋ฅ ==์ผ๋ก ๋น๊ตํด์ผํ๋ค๊ณ ํจ
let score = $('#customRange').val();
let scoreEnum;
if(score == 1){
scoreEnum = "ONE"
}else if(score == 2){
scoreEnum = "TWO"
}else if(score == 3){
scoreEnum = "THREE"
}else if(score == 4){
scoreEnum = "FOUR"
}else if(score == 5){
scoreEnum = "FIVE"
}
์ด๋ ๊ฒ ํด๋ด
์๋ ๋ฆฌ๋ทฐ๊ฐ ์ ์๊ฒจ์…..
์ด๊ฒ Spring์ธก์์ ํ์ ๋ณํ ์ค๋ฅ๊ฐ ์๊ธฐ์ง ์์ผ๋๊น ํ๋ก ํธ ๋ฌธ์ ๋ผ๊ณ ์์ธกํด๋ด
var goodmanner;
var goodmannerbox = $('#goodmanner');
if(goodmannerbox.is(':checked')){
goodmanner = 1;
}else{
goodmanner = 0;
}
var goodtime;
var goodtimebox = $('#goodtime');
if(goodtimebox.is(':checked')){
goodtime = 1;
}else{
goodtime = 0;
}
var goodcomm;
var goodcommbox = $('#goodcomm');
if(goodcommbox.is(':checked')){
goodcomm = 1;
}else{
goodcomm = 0;
}
var badtime;
var badtimebox = $('#goodcomm');
if(badtimebox.is(':checked')){
badtime = 1;
}else{
badtime = 0;
}
var noshow;
var noshowbox = $('#goodcomm');
if(noshowbox.is(':checked')){
noshow = 1;
}else{
noshow = 0;
}
var nomoney;
var nomoneybox = $('#goodcomm');
if(nomoneybox.is(':checked')){
nomoney = 1;
}else{
nomoney = 0;
}
var badcomm;
var badcommbox = $('#goodcomm');
if(badcommbox.is(':checked')){
badcomm = 1;
}else{
badcomm = 0;
}
var badmanner;
var badmannerbox = $('#goodcomm');
if(badmannerbox.is(':checked')){
badmanner = 1;
}else{
badmanner = 0;
}
์ด๋ ๊ฒ ํด๋ด
User 2 ๋ก ๋ก๊ทธ์ธํด์ ํด๋ด
๐ฉ๋ฌธ์ : ๋ฆฌ๋ทฐ๋ฅผ ์์ฑํด๋ db์ ์ ์ฅ๋์ง ์์
Order 5๋ฒ์ ๋ํด์
๋ฆฌ๋ทฐ๋ฅผ ๋ ๋ ค๋ด๋ db์ ์ ์ฅ๋์ง ์์
const is_checked = checkbox.checked;
.checked๋ฅผ ์จ๋ด
var goodmanner;
var goodmannerbox = $('#goodmanner');
if(goodmannerbox.checked){
goodmanner = 1;
}else{
goodmanner = 0;
}
var goodtime;
var goodtimebox = $('#goodtime');
if(goodtimebox.checked){
goodtime = 1;
}else{
goodtime = 0;
}
var goodcomm;
var goodcommbox = $('#goodcomm');
if(goodcommbox.checked){
goodcomm = 1;
}else{
goodcomm = 0;
}
var badtime;
var badtimebox = $('#goodcomm');
if(badtimebox.checked){
badtime = 1;
}else{
badtime = 0;
}
var noshow;
var noshowbox = $('#goodcomm');
if(noshowbox.checked){
noshow = 1;
}else{
noshow = 0;
}
var nomoney;
var nomoneybox = $('#goodcomm');
if(nomoneybox.checked){
nomoney = 1;
}else{
nomoney = 0;
}
var badcomm;
var badcommbox = $('#goodcomm');
if(badcommbox.checked){
badcomm = 1;
}else{
badcomm = 0;
}
var badmanner;
var badmannerbox = $('#goodcomm');
if(badmannerbox.is(':checked')){
badmanner = 1;
}else{
badmanner = 0;
}
์๋ ์ง๊ธ๋ณด๋ ๋ค goodcomm์ผ๋ก ๋๊ฒ๋ ์์ด์ ์์ ํจ
๋ฆฌ๋ทฐ๋ฅผ ๋ณด๋ด๋ณด์๋ค
Score๋ ์ด์ ์ ๋ค์ด๊ฐ
Review๋ ๋ค์ด๊ฐ์ง์ง ์๋๋ค
NOTIME count๊ฐ ์์ฑ๋์ง ์์
function sendReview() {
let score = $('#customRange').val();
let scoreEnum;
if(score == 1){
scoreEnum = "ONE"
}else if(score == 2){
scoreEnum = "TWO"
}else if(score == 3){
scoreEnum = "THREE"
}else if(score == 4){
scoreEnum = "FOUR"
}else if(score == 5){
scoreEnum = "FIVE"
}
var goodmanner;
var goodmannerbox = $('#goodmanner');
if(goodmannerbox.prop('checked')){
goodmanner = 1;
}else{
goodmanner = 0;
}
var goodtime;
var goodtimebox = $('#goodtime');
if(goodtimebox.prop('checked')){
goodtime = 1;
}else{
goodtime = 0;
}
var goodcomm;
var goodcommbox = $('#goodcomm');
if(goodcommbox.prop('checked')){
goodcomm = 1;
}else{
goodcomm = 0;
}
var badtime;
var badtimebox = $('#goodcomm');
if(badtimebox.prop('checked')){
badtime = 1;
}else{
badtime = 0;
}
var noshow;
var noshowbox = $('#goodcomm');
if(noshowbox.prop('checked')){
noshow = 1;
}else{
noshow = 0;
}
var nomoney;
var nomoneybox = $('#goodcomm');
if(nomoneybox.prop('checked')){
nomoney = 1;
}else{
nomoney = 0;
}
var badcomm;
var badcommbox = $('#goodcomm');
if(badcommbox.prop('checked')){
badcomm = 1;
}else{
badcomm = 0;
}
var badmanner;
var badmannerbox = $('#goodcomm');
if(badmannerbox.prop('checked')){
badmanner = 1;
}else{
badmanner = 0;
}
sendData(scoreEnum,goodmanner,goodtime,goodcomm,badtime,noshow,nomoney,badcomm,badmanner);
}
์ด๋ ๊ฒ ํ๊ณ
const goodmannerbox = document.getElementById('goodmanner');
var goodmanner = goodmannerbox.checked ? 1 : 0;
const goodtimebox = document.getElementById('goodtime');
var goodtime = goodtimebox.checked ? 1 : 0;
const goodcommbox = document.getElementById('goodcomm');
var goodcomm = goodcommbox.checked ? 1 : 0;
const badtimebox = document.getElementById('badtime');
var badtime = badtimebox.checked ? 1 : 0;
const noshowbox = document.getElementById('noshow');
var noshow = noshowbox.checked ? 1 : 0;
const nomoneybox = document.getElementById('nomoney');
var nomoney = nomoneybox.checked ? 1 : 0;
const badcommbox = document.getElementById('badcomm');
var badcomm = badcommbox.checked ? 1 : 0;
const badmannerbox = document.getElementById('badmanner');
var badmanner = badmannerbox.checked ? 1 : 0;
์ด๋ ๊ฒ ๋ฐ๊ฟ๋ด
const goodmannerbox = document.getElementById('goodmanner');
var goodmanner = goodmannerbox.checked ? 1 : 0;
const goodtimebox = document.getElementById('goodtime');
var goodtime = goodtimebox.checked ? 1 : 0;
const goodcommbox = document.getElementById('goodcomm');
var goodcomm = goodcommbox.checked ? 1 : 0;
const badtimebox = document.getElementById('badtime');
var badtime = badtimebox.checked ? 1 : 0;
const noshowbox = document.getElementById('noshow');
var noshow = noshowbox.checked ? 1 : 0;
const nomoneybox = document.getElementById('nomoney');
var nomoney = nomoneybox.checked ? 1 : 0;
const badcommbox = document.getElementById('badcomm');
var badcomm = badcommbox.checked ? 1 : 0;
const badmannerbox = document.getElementById('badmanner');
var badmanner = badmannerbox.checked ? 1 : 0;
์ด๋ ๊ฒ ๋ฐ๊ฟ๋ด
๋ค์ ํด๋ด
์ ๋ค์ด์๋ค
์ ์ฒด๊ธ ํ์ด์ง ๋ง๋ค์ด๋ด
// ๋ชจ๋ ๊ธ ์กฐํํ๊ธฐ
@GetMapping("/posts")
public ApiResponse<List<BriefPostResponse>> getPosts(
@AuthenticationPrincipal UserDetailsImpl userDetails) {
return new ApiResponse<>(HttpStatus.OK.value(), "๋ชจ๋ ๊ธ ์กฐํ์ ์ฑ๊ณตํ์ต๋๋ค.",
postService.getPosts(userDetails.getUser()));
}
//๊ธ ์นดํ
๊ณ ๋ฆฌ๋ณ ์กฐํ
@GetMapping("/posts/category")
public ApiResponse<List<BriefPostResponse>> getPostsByCategory(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@Valid @RequestBody PostCategoryRequest postCategorySearchReq
) {
return new ApiResponse<>(HttpStatus.OK.value(), "๊ธ ์นดํ
๊ณ ๋ฆฌ๋ณ ์กฐํ์ ์ฑ๊ณตํ์ต๋๋ค.",
postService.getPostsByCategory(postCategorySearchReq, userDetails.getUser()));
}
public record PostCategoryRequest(@NotNull @Category String category) {
}
function getDataByCategory(categoryEnum){
let category = categoryEnum;
$.ajax({
type: 'GET',
url: `/api/v1/posts/category`,
dataType: "json",
contentType: 'application/json',
data: JSON.stringify({
category: category
}),
success: function (response) {
console.log('Success:', response);
drawAllPosts(response.data);
},
error: function (error) {
console.error('Error:', error);
}
});
}
์ด๋ ๊ฒ ํด๋ด
์ ๊ฑฐ ๋๋ฅด๋ฉด ์๋ก ๊ฒฐ๊ณผ ๋ฐ์์์ ํ๋ฉด์ ์ถ๋ ฅํ ๊ฑด๋ฐ ๊ทธ๋ผ ๊ธฐ์กด ๊ฒฐ๊ณผ๋ ์ญ์ ํด์ผํ์ง ์๋ ์ถ์ด์ removeํจ์ ์์๋ด
https://developer.mozilla.org/en-US/docs/Web/API/Element/remove
๊ทผ๋ฐ ์ด๊ฑฐ ๋ง๊ณ child๋ค์ ๋ค removeํ๊ณ ์ถ์
https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild
<div id="parent">
<div id="child"></div>
</div>
const parent = document.getElementById("parent");
const child = document.getElementById("child");
const throwawayNode = parent.removeChild(child);
๊ทผ๋ฐ ๋๋ child๊ฐ ์ฌ๋ฌ๊ฐ ๋ฐ?
To remove all children from an element:
const element = document.getElementById("idOfParent");
while (element.firstChild) {
element.removeChild(element.firstChild);
}
→
์ด๋ ๊ฒ ํด ๋ด
function removeAllPosts(){
const element = document.getElementById("posts");
while (element.firstChild) {
element.removeChild(element.firstChild);
}
}
function all(){
removeAllPosts();
getData();
}
์ ์ฒด๋ฅผ ๊ณ ๋ฅด๋ฉด ๋ค ์ง์ฐ๊ณ ์๋ก ์ถ๋ ฅํ๊ฒ ํด๋ด
function burger(){
removeAllPosts();
getDataByCategory("BURGER");
}
์ด๋ ๊ฒ ํด๋ด
๋๋จธ์ง๋ ๋๊ฐ์ด ํจ
//๊ธ ๊ฒ์ํ๊ธฐ
@GetMapping("/posts/search")
public ApiResponse<List<BriefPostResponse>> searchPost(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@Valid @RequestBody PostSearchRequest postSearchReq
) {
return new ApiResponse<>(HttpStatus.OK.value(), "๊ฒ์ ๊ฒฐ๊ณผ",
postService.searchPost(postSearchReq, userDetails.getUser()));
}
public record PostSearchRequest(
@NotNull(message = "๊ฒ์์ด๋ฅผ ์
๋ ฅํ์ธ์.")
String keyword
) {
}
์ ๊ฑฐ ๋๋ฅด๋ฉด ์๋ก ๊ฒฐ๊ณผ ๋ฐ์์์ ํ๋ฉด์ ์ถ๋ ฅํ ๊ฑด๋ฐ ๊ทธ๋ผ ๊ธฐ์กด ๊ฒฐ๊ณผ๋ ์ญ์ ํด์ผํ์ง ์๋ ์ถ์ด์ removeํจ์ ์์๋ด
๊ฒ์ ์ ๋ ฅ input๊ณผ ๋ฒํผ์ด ๋ฌ๋ฆฐ ๋ถํธ์คํธ๋ฉ ์ฐพ์๋ค๊ฐ ๋ง๋ฆ
<div class="input-group mb-3">
<input aria-describedby="button-addon2" aria-label="keyword" class="form-control" id="keyword" placeholder="๊ฒ์์ด"
type="text">
<button class="btn btn-outline-secondary" onclick="search()" id="search" type="button">๊ฒ์</button>
</div>
function search(){
var keyword = $('#keyword').val();
removeAllPosts();
getDataByKeyword(keyword);
}
function getDataByKeyword(keyword){
var keyword = keyword;
$.ajax({
type: 'GET',
url: `/api/v1/posts/search`,
dataType: "json",
contentType: 'application/json',
data: JSON.stringify({
keyword: keyword
}),
success: function (response) {
console.log('Success:', response);
drawAllPosts(response.data);
},
error: function (error) {
console.error('Error:', error);
}
});
}
์ผ๋จ ํด๋ดค์
์ฑ๊ณตํ๋๋ฐ ์ ์ฅ๋ ๊ธ ๋ฐ์ดํฐ๊ฐ ํ๋๋ ์์ด์ ํ๋ฉด์ ์ถ๋ ฅ์ ์ ๋จ
๐ฉ ๋ฌธ์ : ajax GET์ ํ ๋ RequestBody๋ก Object๋ฅผ ๋ณด๋ผ ์ ์์
๊ฒ์์ ์๋ํด๋ดค๋๋
์ด๊ฒ ๋ ๋ธ
java.lang.IllegalArgumentException: Invalid character found in the request target [/api/v1/posts/category?{%22category%22:%22BURGER%22} ]. The valid characters are defined in RFC 7230 and RFC 3986 ์ฃผ์ ์์ ์ %22๊ฐ ๋์ฒด ๋ญ๋๋ง์
data: {
keyword: keyword
},
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public com.moayo.moayoeats.backend.global.dto.ApiResponse<java.util.List<com.moayo.moayoeats.backend.domain.post.dto.response.BriefPostResponse>> com.moayo.moayoeats.backend.domain.post.controller.PostController.searchPost(com.moayo.moayoeats.backend.global.security.UserDetailsImpl,com.moayo.moayoeats.backend.domain.post.dto.request.PostSearchRequest)]
๊ธ ํ๋ ๋ง๋ค์ด๋ดค๋๋ ๊ทธ๊ฑด ์ ๋ธ
์นดํ ๊ณ ๋ฆฌ ํ์ ๋๋ ๋๋ ์ด๊ฑฐ ๋ธ
java.lang.IllegalArgumentException: Invalid character found in the request target [/api/v1/posts/category?{%22category%22:%22KOREAN%22} ]. The valid characters are defined in RFC 7230 and RFC 3986
๊ฒ์๋ฒํผ ๋๋ฌ๋ดค๋๋ ๊ทธ๊ฑด ์ด๊ฑฐ ๋ธ
2024-01-19T20:43:50.924+09:00 WARN 29456 --- [nio-8080-exec-5] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public com.moayo.moayoeats.backend.global.dto.ApiResponse<java.util.List<com.moayo.moayoeats.backend.domain.post.dto.response.BriefPostResponse>> com.moayo.moayoeats.backend.domain.post.controller.PostController.searchPost(com.moayo.moayoeats.backend.global.security.UserDetailsImpl,com.moayo.moayoeats.backend.domain.post.dto.request.PostSearchRequest)]
์คํ์ค๋ฒํ๋ก์ฐ์ ์ด๋ค ์ฌ๋์ด
Did you mean: URL2 = JSON.stringify(URL), and then URL2 = encodeURI(JSON.stringify(URL2))
์ด๊ฑฐ ํด๋ณด๋ผ๊ณ ํจ
function getDataByCategory(categoryEnum) {
let category = categoryEnum;
let URL = JSON.stringify({
category: category
})
let categoryUrl= encodeURI(JSON.stringify(URL))
$.ajax({
type: 'GET',
url: `/api/v1/posts/category?${categoryUrl}`,
dataType: "json",
contentType: 'application/json',
data: {},
/*
JSON.stringify({
category: category
}),*/
success: function (response) {
console.log('Success:', response);
drawAllPosts(response.data);
},
error: function (error) {
console.error('Error:', error);
}
});
}
์ด๊ฑฐ ํด๋ด
2024-01-19T20:54:15.791+09:00 WARN 14536 --- [nio-8080-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public com.moayo.moayoeats.backend.global.dto.ApiResponse<java.util.List<com.moayo.moayoeats.backend.domain.post.dto.response.BriefPostResponse>> com.moayo.moayoeats.backend.domain.post.controller.PostController.getPostsByCategory(com.moayo.moayoeats.backend.global.security.UserDetailsImpl,com.moayo.moayoeats.backend.domain.post.dto.request.PostCategoryRequest)]
์ ๋จ…
์์ ๊ฐ์ฒด ์ทจ๊ธ์ ์ ํด์ค
์ด์ฌ๋์ ๋ฌธ์ ๋์ ๋์ผํ๋ค
According to https://tomcat.apache.org/tomcat-8.5-doc/config/systemprops.html, requestTargetAllow is deprecated. For me, the other solutions presented here did not work either. According to the Tomcat documentation I found a way to set the property relaxedQueryChars instead:
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
connector.setProperty("relaxedQueryChars", "|{}[]");
}
});
return factory;
}
18
For a Spring Boot application, just add to properties file:
server.tomcat.relaxed-query-chars=|,{,},[,]
There is also the following key: server.tomcat.relaxed-path-chars
@Configuration
public class WebServerConfig {
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
connector.setProperty("relaxedQueryChars", "|{}[]");
}
});
return factory;
}
}
ํ๋ฒ ์ด๋ ๊ฒ ํด๋ด
44.858+09:00 WARN 33020 --- [nio-8080-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public com.moayo.moayoeats.backend.global.dto.ApiResponse<java.util.List<com.moayo.moayoeats.backend.domain.post.dto.response.BriefPostResponse>> com.moayo.moayoeats.backend.domain.post.controller.PostController.getPostsByCategory(com.moayo.moayoeats.backend.global.security.UserDetailsImpl,com.moayo.moayoeats.backend.domain.post.dto.request.PostCategoryRequest)]
024-01-19T21:11:18.234+09:00 WARN 33020 --- [nio-8080-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public com.moayo.moayoeats.backend.global.dto.ApiResponse<java.util.List<com.moayo.moayoeats.backend.domain.post.dto.response.BriefPostResponse>> com.moayo.moayoeats.backend.domain.post.controller.PostController.searchPost(com.moayo.moayoeats.backend.global.security.UserDetailsImpl,com.moayo.moayoeats.backend.domain.post.dto.request.PostSearchRequest)]
Required request body is missing:
ํ ๋ชจ๋ฅด๊ฒ ๋ค
์๊น๋ ๊ทธ๋ฌ๋๋ฐ POST๋ jsonํ์์ผ๋ก ์ ๋๋๋ฐ get์ ์ ๋ผ์
Q. ์ ์๋์ง? POST๋ ์ ๋๋๊ฑฐ์ง?
๊ทธ๋ฅ requestParam์ผ๋ก ์์ ํด์ผํ ๊ฑฐ ๊ฐ์
https://www.baeldung.com/spring-request-param
โณ ํด๊ฒฐ : Get method RequestBody์์ RequestParam ๋ฐฉ์์ผ๋ก ๋ชจ๋ ๋ณ๊ฒฝํจ
@Override
public List<BriefPostResponse> searchPost(String keyword, User user) {
//get all posts filtered by search keyword
List<Post> posts = postRepository.findPostByStoreContaining(keyword)
.orElse(null);
//List<Post> -> List<BriefPostResponse>
return postsToBriefResponses(posts);
}
String keyword๋ก ๋ค ๋ณ๊ฒฝํจ
//๊ธ ๊ฒ์ํ๊ธฐ
@GetMapping("/posts/search")
public ApiResponse<List<BriefPostResponse>> searchPost(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@RequestParam String keyword
) {
return new ApiResponse<>(HttpStatus.OK.value(), "๊ฒ์ ๊ฒฐ๊ณผ",
postService.searchPost(keyword, userDetails.getUser()));
}
@Override
public List<BriefPostResponse> searchPost(String keyword, User user) {
//get all posts filtered by search keyword
List<Post> posts = postRepository.findPostByStoreContaining(keyword)
.orElse(null);
//List<Post> -> List<BriefPostResponse>
return postsToBriefResponses(posts);
}
$.ajax({
type: 'GET',
url: `/api/v1/posts/search?keyword=${keyword}`,
dataType: "json",
contentType: 'application/json',
data: {},
success: function (response) {
console.log('Success:', response);
drawAllPosts(response.data);
},
error: function (error) {
console.error('Error:', error);
}
});
์ด๊ฑธ๋ก ํด๋ณด๊ธฐ
์ ๋จ
์นดํ ๊ณ ๋ฆฌ๋ ๋ฐ๊ฟ๋ด
//๊ธ ์นดํ
๊ณ ๋ฆฌ๋ณ ์กฐํ
@GetMapping("/posts/category")
public ApiResponse<List<BriefPostResponse>> getPostsByCategory(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@RequestParam CategoryEnum category
) {
return new ApiResponse<>(HttpStatus.OK.value(), "๊ธ ์นดํ
๊ณ ๋ฆฌ๋ณ ์กฐํ์ ์ฑ๊ณตํ์ต๋๋ค.",
postService.getPostsByCategory(category, userDetails.getUser()));
}
@Override
public List<BriefPostResponse> getPostsByCategory(CategoryEnum category,
User user) {
List<Post> posts;
if (category.equals(CategoryEnum.ALL.toString())) {
posts = findAll();
} else {
posts = postRepository.findAllByCategoryEquals(category.toString()).orElse(null);
}
return postsToBriefResponses(posts);
}
Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'category' for method parameter type CategoryEnum is not present]
์ html๋ฐ๊พธ๋๊ฑฐ ๊น๋จน์
function getDataByCategory(categoryEnum) {
let category = categoryEnum;
$.ajax({
type: 'GET',
url: `/api/v1/posts/category?category=${category}`,
dataType: "json",
contentType: 'application/json',
data:{},
success: function (response) {
console.log('Success:', response);
drawAllPosts(response.data);
},
error: function (error) {
console.error('Error:', error);
}
});
}
org.hibernate.query.QueryArgumentException: Argument [BURGER] of type [java.lang.String] did not match parameter type [com.moayo.moayoeats.backend.domain.post.entity.CategoryEnum
function getDataByCategory(categoryEnum) {
let category = categoryEnum;
$.ajax({
type: 'GET',
url: `/api/v1/posts/category`,
dataType: "json",
contentType: 'application/json',
data:{category:category},
success: function (response) {
console.log('Success:', response);
drawAllPosts(response.data);
},
error: function (error) {
console.error('Error:', error);
}
});
}
์ด๋ ๊ฒ ํด๋ด
org.hibernate.query.QueryArgumentException: Argument [CHICKEN] of type [java.lang.String] did not match parameter type [com.moayo.moayoeats.backend.domain.post.entity.CategoryEnum (n/a)] at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:85) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final] at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:32) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final] at org.hibernate.query.internal.QueryParameterBindingImpl.validate(QueryParameterBindingImpl.java:362) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final] at org.hibernate.query.internal.QueryParameterBindingImpl.setBindValue(QueryParameterBindingImpl.java:137) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final] at org.hibernate.query.spi.AbstractCommonQueryContract.setParameter(AbstractCommonQueryContract.java:1047) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final] at org.hibernate.query.spi.AbstractSelectionQuery.setParameter(AbstractSelectionQuery.java:966) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final] at org.hibernate.query.sqm.internal.QuerySqmImpl.setParameter(QuerySqmImpl.java:1324) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final] at org.hibernate.query.sqm.internal.QuerySqmImpl.setParameter(QuerySqmImpl.java:140) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
์๋๋๋ด
function getDataByCategory(categoryEnum) {
let category = categoryEnum;
$.ajax({
type: 'GET',
url: `/api/v1/posts/category?category=${category}`,
dataType: "json",
contentType: 'application/json',
data:{},
success: function (response) {
console.log('Success:', response);
drawAllPosts(response.data);
},
error: function (error) {
console.error('Error:', error);
}
});
}
ใ ใ ใ ใ ใ ใ ใ ใ ใ
//๊ธ ์นดํ
๊ณ ๋ฆฌ๋ณ ์กฐํ
@GetMapping("/posts/category")
public ApiResponse<List<BriefPostResponse>> getPostsByCategory(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@RequestParam @Category String category
) {
return new ApiResponse<>(HttpStatus.OK.value(), "๊ธ ์นดํ
๊ณ ๋ฆฌ๋ณ ์กฐํ์ ์ฑ๊ณตํ์ต๋๋ค.",
postService.getPostsByCategory(category, userDetails.getUser()));
}
์ผ๋จ ์ ํด์ง๊ฑฐ ํด๋ณด๊ณ html์์ ์ด์ํ๊ฒ๋ ๋ณด๋ด๋ด์ผ์ง
org.hibernate.query.QueryArgumentException: Argument [ASIAN] of type [java.lang.String] did not match parameter type [com.moayo.moayoeats.backend.domain.post.entity.CategoryEnum (n/a)] at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:85) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
at com.moayo.moayoeats.backend.domain.post.service.impl.PostServiceImpl.getPostsByCategory(PostServiceImpl.java:138) ~[main/:na] at com.moayo.moayoeats.backend.domain.post.controller.PostController.getPostsByCategory(PostController.java:78) ~[main/:na]
Optional<List<Post>> findAllByCategoryEquals(String category);
String์ธ๋ฐ ๋ญ์๋ฆฌํจ
@Override
public List<BriefPostResponse> getPostsByCategory(String category,
User user) {
List<Post> posts;
if (category.equals(CategoryEnum.ALL.toString())) {
posts = findAll();
} else {
posts = postRepository.findAllByCategoryEquals(category).orElse(null);
}
return postsToBriefResponses(posts);
}
์ด๊ฒ๋ String์ด๊ณ
//๊ธ ์นดํ
๊ณ ๋ฆฌ๋ณ ์กฐํ
@GetMapping("/posts/category")
public ApiResponse<List<BriefPostResponse>> getPostsByCategory(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@RequestParam @Category String category
) {
return new ApiResponse<>(HttpStatus.OK.value(), "๊ธ ์นดํ
๊ณ ๋ฆฌ๋ณ ์กฐํ์ ์ฑ๊ณตํ์ต๋๋ค.",
postService.getPostsByCategory(category, userDetails.getUser()));
}
๋ค String์ธ๋ฐ?
๊ทผ๋ฐ Entity์ ์ ์ฅ๋ ํ์ ์ CategoryEnum์ด๋ผ ๊ทธ ๋ถ๋ถ ๋ฐ๊ฟ๋ด
Optional<List<Post>> findAllByCategoryEquals(CategoryEnum category);
@Override
public List<BriefPostResponse> getPostsByCategory(String category,
User user) {
List<Post> posts;
CategoryEnum categoryEnum = CategoryEnum.valueOf(category);
if (category.equals(CategoryEnum.ALL.toString())) {
posts = findAll();
} else {
posts = postRepository.findAllByCategoryEquals(categoryEnum).orElse(null);
}
return postsToBriefResponses(posts);
}
์ ๋๋ค
์ด์ @Category ๊ฐ ๋๋์ง ๋ณด๊ธฐ ์ํด html์ ๋ฐ๊ฟ์ ํ ์คํธํด๋ด
function getDataByCategory(categoryEnum) {
let category = categoryEnum;
$.ajax({
type: 'GET',
url: `/api/v1/posts/category?category=${category}`,
dataType: "json",
contentType: 'application/json',
data:{},
success: function (response) {
console.log('Success:', response);
drawAllPosts(response.data);
},
error: function (error) {
console.error('Error:', error);
}
});
}
→
function getDataByCategory(categoryEnum) {
let category = categoryEnum;
$.ajax({
type: 'GET',
url: `/api/v1/posts/category?category=test`,
dataType: "json",
contentType: 'application/json',
data:{},
success: function (response) {
console.log('Success:', response);
drawAllPosts(response.data);
},
error: function (error) {
console.error('Error:', error);
}
});
}
์นดํ ๊ณ ๋ฆฌ ๋๋ฅผ๋๋ง๋ค ์๋ฌ๊ฐ ๋ธ!
ํฌ์คํธ๋งจ์์๋ ํด๋ด
localhost:8080/api/v1/posts/category?category=test
์ด๋ ๊ฒ ๋ณด๋ด๋ฉด
403Forbidden
์ด๋ ๊ฒ ๋จ๊ณ
localhost:8080/api/v1/posts/category?category=ALL
์ด๋ ๊ฒ ๋ณด๋ด๋ฉด ์ ๋ธ!
{
"status": 200,
"message": "๊ธ ์นดํ
๊ณ ๋ฆฌ๋ณ ์กฐํ์ ์ฑ๊ณตํ์ต๋๋ค.",
"data": [
{
"id": 34,
"author": "๊ฐ๋๋ค๋ผ",
"address": "37.5675458,126.9714466",
"store": "1",
"minPrice": 1,
"sumPrice": 0,
"deadline": "2024-01-19T23:42:31"
}
]
}
์ด๊ฑด ๋ฌด์จ ์๋ฌ๋ฅผ ๋์ง๋์ง ๋จ์ง๋ ์์์ ์ด๋ป๊ฒ Exception ์ฒ๋ฆฌํด์ผํ ์ง ๋ชจ๋ฅด๊ฒ ๋ค!
→ ๐issue: ๋์ค์ ์ถ๊ฐ๋ก ํด๋ณด๊ธฐ!!!
'Developing > ๊ฐ๋ฐ์ผ์ง' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
2024-01-18, Today I Learned (0) | 2024.01.18 |
---|---|
2024-01-17, Today I Learned (0) | 2024.01.17 |
2024-01-16, Today I Learned (0) | 2024.01.16 |
2024-01-15, Today I Learned (0) | 2024.01.15 |
2024-01-14, Today I Learned (0) | 2024.01.15 |