[스프링부트] 4/4 목록 구현

최경현·2023년 12월 25일
0

평점을 이용하였음.

와인 정보 게시판을 예시로 구현하였음.
4/4분면 값 저장을 위해

entity 작성

import com.std.sbb.domain.wine.entity.Wine;
import com.std.sbb.global.jpa.BaseEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.OneToOne;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import lombok.*;
import lombok.experimental.SuperBuilder;

@Entity
@Getter
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Taste extends BaseEntity {
    @Min(value = 1)
    @Max(value = 5)
    private Integer sweet;
    @Min(value = 1)
    @Max(value = 5)
    private Integer body;
    @Min(value = 1)
    @Max(value = 5)
    private Integer acidity;
    @Min(value = 1)
    @Max(value = 5)
    private Integer tannin;
    @OneToOne(mappedBy = "taste")
    private Wine wine;
}

BaseEntity 작성

import jakarta.persistence.*;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

import static lombok.AccessLevel.PROTECTED;

@Getter
@SuperBuilder
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor(access = PROTECTED)
@ToString
@EqualsAndHashCode
public abstract class BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @EqualsAndHashCode.Include
    private Long id;
    @CreatedDate
    private LocalDateTime createDate;
    @LastModifiedDate
    private LocalDateTime modifyDate;
}

4/4분면 값을 저장하기 위한

service 구문 작성

import com.std.sbb.domain.taste.entity.Taste;
import com.std.sbb.domain.taste.repository.TasteRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
@RequiredArgsConstructor
public class TasteService {

    private final TasteRepository tasteRepository;

    public Taste create(Integer sweet, Integer body, Integer acidity, Integer tannin) {
        Taste taste = Taste.builder()
                .sweet(sweet)
                .body(body)
                .acidity(acidity)
                .tannin(tannin)
                .build();
        this.tasteRepository.save(taste);
        return taste;
    }
    public Taste getTaste(Long id) {
        Optional<Taste> ot = this.tasteRepository.findById(id);
        if (ot.isPresent()) {
            return ot.get();
        } else {
            throw new RuntimeException("맛이 존재하지 않습니다");
        }
    }
}

repository구문과 form구문을 작성

import com.std.sbb.domain.taste.entity.Taste;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface TasteRepository extends JpaRepository<Taste, Long> {
}

form

import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.Getter;

@Data
@Getter
public class TasteForm{
    @NotNull(message = "Please provide a value for sweet")
    private Integer sweet;
    @NotNull(message = "Please provide a value for body")
    private Integer body;
    @NotNull(message = "Please provide a value for acidity")
    private Integer acidity;
    @NotNull(message = "Please provide a value for tannin")
    private Integer tannin;
}

4/4분면 값을 wine 값에 종속시키기 위해 wineEntity에 코드 추가

 @OneToOne
    private Taste taste;

wineController에도 구문 추가

Taste taste = tasteService.create(tasteForm.getSweet(), tasteForm.getBody(), tasteForm.getAcidity(), tasteForm.getTannin());

 this.wineService.create(wineForm.getWineName(), wineForm.getWineNameE(), wineForm.getCountry(),
                    wineForm.getList(), wineForm.getPrice(), wineForm.getKind(), wineForm.getFood(),
                    wineForm.getScore(), board, taste);

service에도 추가시켜줘야함

버튼 클릭 유지를 위해 스크립트 구문 추가

<script th:inline="javascript">

    document.addEventListener('DOMContentLoaded', function () {
        var dateInput = document.querySelector('[datepicker]');
        flatpickr(dateInput, {
            dateFormat: 'm/d/Y',
        });
    });

    $(document).ready(function () {
        // 페이지 로드 시 저장된 클릭 상태를 가져와 설정
        $('[name^="score-"]').each(function () {
            var tasteId = $(this).attr('id');
            var savedValue = localStorage.getItem('selectedValue-' + tasteId);

            if (savedValue === $(this).val()) {
                $(this).prop('checked', true);
            }
        });

        // 각각의 맛 구성 요소에 대한 독립적인 클릭 및 클릭 상태 유지
        $('[name^="score-"]').on('change', function () {
            // 클릭한 라디오 버튼이 이미 체크된 경우, 다시 체크하지 않도록 함
            if (!$(this).prop('checked')) {
                $(this).prop('checked', true);
            }

            var tasteId = $(this).attr('id');
            var selectedValue = $(this).val();

            // 클릭한 값 저장
            localStorage.setItem('selectedValue-' + tasteId, selectedValue);
        });
    });
</script>

html구문 작성

<div th:fragment="tasteFragment(tasteId)">
    <div class="mx-5 flex flex-row-reverse justify-end text-4xl">
        <label th:for="${'score-' + tasteId}"></label>
        <input type="radio" class="peer hidden" th:id="${'value5-' + tasteId}" th:value="5"
               th:name="${fieldName}" th:checked="${tasteForm != null && tasteForm[fieldName] == 5}"/>
        <label th:for="${'value5-' + tasteId}"
               class="cursor-pointer text-gray-400 peer-hover:text-pink-600 peer-checked:text-pink-600"></label>

        <input type="radio" class="peer hidden" th:id="${'value4-' + tasteId}" th:value="4"
               th:name="${fieldName}" th:checked="${tasteForm != null && tasteForm[fieldName] == 4}"/>
        <label th:for="${'value4-' + tasteId}"
               class="cursor-pointer text-gray-400 peer-hover:text-pink-500 peer-checked:text-pink-500"></label>

        <input type="radio" class="peer hidden" th:id="${'value3-' + tasteId}" th:value="3"
               th:name="${fieldName}" th:checked="${tasteForm != null && tasteForm[fieldName] == 3}"/>
        <label th:for="${'value3-' + tasteId}"
               class="cursor-pointer text-gray-400 peer-hover:text-pink-400 peer-checked:text-pink-400"></label>

        <input type="radio" class="peer hidden" th:id="${'value2-' + tasteId}" th:value="2"
               th:name="${fieldName}" th:checked="${tasteForm != null && tasteForm[fieldName] == 2}"/>
        <label th:for="${'value2-' + tasteId}"
               class="cursor-pointer text-gray-400 peer-hover:text-pink-300 peer-checked:text-pink-300"></label>

        <input type="radio" class="peer hidden" th:id="${'value1-' + tasteId}" th:value="1"
               th:name="${fieldName}" th:checked="${tasteForm != null && tasteForm[fieldName] == 1}"/>
        <label th:for="${'value1-' + tasteId}"
               class="cursor-pointer peer text-gray-400 peer-hover:text-pink-200 peer-checked:text-pink-200"></label>
    </div>
</div>
<div class="flex items-center my-4" th:field="*{taste}">
            <span class="flex items-center">당도</span>
            <div th:replace="~{taste1 :: tasteFragment(tasteId='sweet', fieldName='sweet')}"></div>
            <span class="flex items-center">산도</span>
            <div th:replace="~{taste1 :: tasteFragment(tasteId='acidity', fieldName='acidity')}"></div>
            <span class="flex items-center">바디</span>
            <div th:replace="~{taste1 :: tasteFragment(tasteId='body', fieldName='body')}"></div>
            <span class="flex items-center">타닌</span>
            <div th:replace="~{taste1 :: tasteFragment(tasteId='tannin', fieldName='tannin')}"></div>
            <button class="text-white bg-gray-800 hover:bg-gray-900 focus:outline-none focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 ml-20">
                게시글 등록
            </button>
        </div>
profile
ㅇㅇ

0개의 댓글