본문 바로가기

항해99/스터디

[스터디] 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 - 머스테치로 화면 구성하기

머스테치와 Thymeleaf 차이점

 

1. 구문

  • Thymeleaf: HTML과 완벽하게 통합되는 정교한 구문을 가지고 있음 -> th:text, th:if, th:each 등과 같이 th: 접두사가 붙은 속성을 사용하여 변수 대체, 반복 및 조건부 렌더링과 같은 작업을 수행함
  • Mustache: 간단하고 직관적인 구문을 사용. 논리가 없고 변수 대체를 위해 {{ }}, 조건부 렌더링을 위해 {{# }} 및 {{^ }}와 같은 태그를 사용함.

2. 특징

  • Thymeleaf: 조건문, 반복, 국제화, 조각 포함, 양식 바인딩 등을 포함한 광범위한 기능을 제공함 (Mustache에 비해 기능이 더 풍부)
  • Mustache: 의도적으로 단순하게 유지되었고, 고급 기능들을 지원하지 않음. 주로 논리 없이 데이터를 렌더링하는 데 중점을 둠

3. 프레임워크와의 통합

  • Thymeleaf: 일반적으로 Spring Boot와 같은 Java 기반 웹 프레임워크와 함께 사용함. Spring MVC와 긴밀한 통합을 제공하므로 서버측 Java 애플리케이션 작업이 쉬워짐
  • Mustache : 다양한 프레임워크 및 언어와 통합될 수 있음 Angular, React와 같은 JavaScript 프레임워크 또는 Node.js와 같은 서버 측 언어와 함께 사용되는 경우가 많음

4. 성능

  • Thymeleaf : 다양한 기능 복잡한 템플릿이나 대규모 애플리케이션을 처리할 때 성능에 영향을 줄 수 있음. 그러나 캐싱 메커니즘은 성능 문제를 완화하는 데 도움을 줌
  • Mustache : 단순성과 가벼운 특성으로 잘 알려져 있고, 특히 간단한 렌더링 작업의 경우 더 나은 성능을 발휘하는 경우가 많음

5. 학습

  • Thymeleaf : 광범위한 기능들과 Java 프레임워크와의 통합은 (특히 Java 웹 개발을 처음 접하는 개발자의 경우) 학습이 어렵게 느껴질 수 있음
  • Mustache : 단순함 때문에 배우고 사용하기가 쉬움. 개발자는 해당 구문을 빠르게 파악하고 템플릿 목적으로 사용이 가능함

QueryDSL

 

1. 타입 안정성이 보장

단순한 문자열로 쿼리를 생성하는 것이 아니라, 메소드를 기반으로 쿼리를 생성하기 때문에 오타나 존재하지 않는 컬럼명을 명시할 경우 IDE에서 자동으로 검출 -> MyBatis는 지원하지 않음

 

2. 국내 많은 회사들이 사용

JPA를 적극적으로 사용하는 회사(배민, 쿠팡 등)에서는 쿼리DSL을 적극적으로 사용중

 

3. 레퍼런스가 많음

국내 많은 회사들과 개발자들이 이용하다 보니까 그만큼 정보가 많고, 커뮤니티에서 질문하고 쉽게 답변을 얻을 수 있음

 

4. ORM(객체 관계형 매핑) 프레임워크와의 통합

Java Hibernate 또는 .NET Entity Framework 같은 많은 ORM 프레임워크는 도구 집합의 일부로 쿼리 DSL 제공 -> 객체 지향 프로그래밍 환경에서 쿼리 작성 데이터베이스 작업 프로세스를 단순화


LV.1 피드백 내용

 

1~2

기존 명세서
수정된 명세서(리스폰스, 리퀘스트는 수정 x)

 

3~4

ERD 작성 방법을 아예 숙지하지 못했고, 인터넷에 돌아다니는 예시들 참고해서 만들다보니까 안 맞는 부분이 많았음

-> 현재는 엔티티 생성 후 인텔리제이 다이어그램으로 ERD 확인함

 

5. 강의에서 항상 Setter 메서드를 붙여서 같이 붙였었음. -> 지금은 Setter 사용하지 않고, Builder 사용

 

6. 그냥 아무 생각 없이 강의에서 항상 한 개만 만들었으니까 한 개만 생성함(..ㅎ)

PostRequestDto updatePost 직접 전달하는 대신 업데이트 작업에 별도의 DTO 생성해서 하는게 좋다는 것을 이제는 안다...

package com.sparta.board.postservice;

import com.sparta.board.dto.PostRequestDto;
import com.sparta.board.dto.PostResponseDto;
import com.sparta.board.entity.Post;
import com.sparta.board.entity.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class PostService {
    private final PostRepository postRepository;

    // 생성자를 통한 의존성 주입
    public PostService(PostRepository postRepository){
        this.postRepository = postRepository;
    }

    // 게시물 생성
    @Transactional
    public PostResponseDto createPost(PostRequestDto requestDto){
        // RequestDto -> 게시물 객체 생성
        Post post = new Post(requestDto);

        // Db에 저장해주기
        Post savePost = postRepository.save(post);

        return new PostResponseDto(savePost);
    }

    // 게시물 조회
    public PostResponseDto getPost(Long id) {
        Post post = postRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("해당 게시물은 존재하지 않습니다. id = " + id));
        return new PostResponseDto(post);
    }

    // 모든 게시물 조회
    public List<PostResponseDto> getPosts(){
        return postRepository.findAllByOrderByModifiedAtDesc().stream().map(PostResponseDto::new).toList();
    }

    // 게시물 수정
    @Transactional
    public PostResponseDto updatePost(Long id, String password, PostRequestDto requestDto){
        // 게시물 존재여부 확인하기 - DB
        Post post = postRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("해당 게시물은 존재하지 않습니다. id=" + id));
        if (!post.getPassword().equals(password)){
            throw new IllegalArgumentException("잘못된 비밀번호 입니다.");
        }
        post.update(requestDto);
        return new PostResponseDto(post);
    }

    // 게시물 삭제
    @Transactional
    public Long deletePost(Long id, String password){
        // 게시물 존재여부 확인하기 - DB
        Post post = postRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("해당 게시물은 존재하지 않습니다. id=" + id));
        if (!post.getPassword().equals(password)){
            throw new IllegalArgumentException("잘못된 비밀번호 입니다.");
        }
        postRepository.delete(post);
        return id;
    }
}
package com.sparta.board.postservice;

import com.sparta.board.dto.PostRequestDto;
import com.sparta.board.dto.PostResponseDto;
import com.sparta.board.entity.Post;
import com.sparta.board.entity.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional
public class PostService {
    private final PostRepository postRepository;

    public PostService(PostRepository postRepository) {
        this.postRepository = postRepository;
    }

    // 게시물 생성
    public PostResponseDto createPost(PostRequestDto requestDto) {
        // 요청 DTO를 이용하여 게시물 객체 생성
        Post post = new Post(requestDto);

        // DB에 게시물 저장
        Post savedPost = postRepository.save(post);

        //  DTO 반환
        return new PostResponseDto(savedPost);
    }

    // 게시물 조회
    public PostResponseDto getPost(Long id) {
        
        Post post = getPostById(id);

        // 조회된 게시물 DTO 반환
        return new PostResponseDto(post);
    }

    // 모든 게시물 조회
    public List<PostResponseDto> getPosts() {
        
        List<Post> posts = postRepository.findAllByOrderByModifiedAtDesc();

        // 조회된 게시물 DTO 리스트 반환
        return posts.stream().map(PostResponseDto::new).toList();
    }

    // 게시물 수정
    @Transactional(rollbackFor = Exception.class)
    public PostResponseDto updatePost(Long id, String password, PostRequestDto requestDto) {
        
        Post post = getPostById(id);

        
        validatePassword(post, password);

        
        post.update(requestDto);

        //  DTO 반환
        return new PostResponseDto(post);
    }

    // 게시물 삭제
    @Transactional(rollbackFor = Exception.class)
    public Long deletePost(Long id, String password) {
        
        Post post = getPostById(id);

        
        validatePassword(post, password);

        // 게시물 삭제
        postRepository.delete(post);

        // 삭제된 게시물의 ID 반환
        return id;
    }

    // ID로 게시물 조회하는 메서드
    private Post getPostById(Long id) {
        return postRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("해당 ID의 게시물을 찾을 수 없습니다: " + id));
    }

    // 비밀번호 검증 메서드
    private void validatePassword(Post post, String password) {
        if (!post.getPassword().equals(password)) {
            throw new IllegalArgumentException("잘못된 비밀번호입니다.");
        }
    }
}