Project

블로그 만들기(2) 아쉬운점

코딩이 어려운 사람 2024. 7. 16. 14:49

일단 우여곡절 끝에 최대한 완성을 하긴 했는데 이번에 프로젝트를 진행하면서 아쉬운 점이 너무 많았다

우선 아쉬운 점들을 보고 코드나 기능적으로 어디가 부족했는지 알아보자

 

아쉬운 점들

1. Rest 하게 만든 건지 아닌지 방식이 너무 혼용됐다.

일단 프런트 영역은 거의 문외한이기에 gpt에 의존하긴 했지만 백 부분에서는 데이터만 넘겨주면 되는데 초반엔 타임리프로 모델에 바인딩하는 식으로 모델을 넘겨주고 후반엔 RestController를 이용해 데이터만 넘기는 방식으로 섞여버렸다 차라리 타임리프로 다했으면 진행하다 헷갈리는 것도 덜 했을 거 같다.

 @PostMapping("/create")
    public String createBlog(@ModelAttribute BlogDto blogDto, @AuthenticationPrincipal UserDetails userDetails, RedirectAttributes redirectAttributes) {
        Optional<Blog> blog = blogService.findBlogByUserLoginId(userDetails.getUsername());
        if (blog.isEmpty()) {
            Blog createdBlog = blogService.createBlog(blogDto, userDetails.getUsername());
            return "redirect:/api/blogs/" + createdBlog.getId();
        }
        redirectAttributes.addFlashAttribute("error", "이미 블로그가 존재 합니다.");
        return "redirect:/";
    }

여기서는 일반 Controller로 처리하고 아래의 메서드는 또 RestController에서 만들어 프로젝트에서 일관성이 하나도 없는 것 같다.

@PostMapping
    public ResponseEntity<String> createComment(@PathVariable("postId") Long postId, @RequestBody CommentCreateDto commentCreateDto, @AuthenticationPrincipal UserDetails userDetails) {
        if (userDetails == null) {
            throw new UnauthorizedException("로그인 후 이용 가능합니다.");
        }
        commentService.createComment(commentCreateDto, userDetails.getUsername(), postId);
        return ResponseEntity.ok("댓글 생성 완료");
    }

 

 

2. 너무나도 부족한 프런트 실력

프런트 코드는 보면 어떻게 굴러가는지는 대충은 알지만 막상 작성하라 하면 작성할 능력은 없기에 gpt를 이용해 거의 작성했다. 어차피 결국은 프런트를 알아야 백엔드에 대한 이해도도 높아질 것이라 생각돼 이번 기회에 프런트도 공부해 보고 리액트도 살짝 공부해 볼 생각이다.

 

 

3. 코드에 일정한 패턴이 없다

비슷한 CRUD에서는 그래도 좀 비슷하게 파라미터를 받는식으로 일정한 패턴이 존재할 수 있을 텐데 너무 의식의 흐름대로 코드를 짜서 같은 Create여도 어디서는 파라미터 하나만 받고 어디서는 3개 받고 이런 식으로 중구난방으로 코드가 짜져 있다. 이번엔 이렇게 실수를 했으니 다음엔 아무 생각 없이 코드를 짜면 안 될 것이다. 그리고 이렇게 패턴이 없으니 가독성도 정말 떨어지는 것 같다.

public Comment createComment(CommentCreateDto commentCreateDto, String loginId, Long postId) {
        User user = userRepository.findByLoginId(loginId).orElseThrow(() -> new UserNotFoundException("해당 사용자를 찾을 수 없습니다."));
        Post post = postRepository.findById(postId).orElseThrow(() -> new PostNotFoundException("해당 포스트를 찾을 수 없습니다."));
        Comment comment = new Comment();
        comment.setUser(user);
        comment.setPost(post);
        comment.setContent(commentCreateDto.getContent());

        return commentRepository.save(comment);
    }

파라미터를 비슷하게 받을 수 있게 좀 일관되게 만들 수 있었을텐데 너무 의식의 흐름대로 코드들이 따로 놀고 있다.

@Transactional
    public Post createPost(PostRequestDto postRequestDto, User user) {
        Blog blog = blogRepository.findById(user.getBlog().getId())
                .orElseThrow(() -> new PostNotFoundException("블로그를 찾을 수 없습니다."));
        Post post = new Post();
        post.setTitle(postRequestDto.getTitle());
        post.setContent(postRequestDto.getContent());
        post.setBlog(blog);
        post.setUser(user);
        post.setTemporal(postRequestDto.isTemporal());
        List<String> tagNames = postRequestDto.getTags();
        List<Tag> tags = new ArrayList<>();
        if (tagNames != null && !tagNames.isEmpty()) {
            tags = tagService.findOrCreateTags(tagNames);
        }
        post.setTags(tags);
        post.setSeries(postRequestDto.getSeries());
        postRepository.save(post);
        log.info("게시글 생성 Id : {}", post.getId());
        return post;
    }

4. DTO 활용이 부족하다.

DTO를 좀더 종류별로 세분화해서 여러 개 만들어야 하는데 Blog에서는 BlogDto 하나로 퉁쳐버리는 식으로 진행했다. 이것도 의식의 흐름대로 코드를 짜서 이렇게 됐는데 이럴 거면 필요한 정보만 넘기기 위해 DTO를 사용하는 건데 이런 식으로 진행할 시 DTO를 사용하는 의미가 크게 느껴지지 않는 것 같아 문제가 되는 것 같다.

 

 

5. 과도한 Setter 사용

사실 개인프로젝트이기에 setter를 열어놔도 큰 문제는 안되겠지만 강의를 따로 듣다가 알았는데 setter를 마구잡이로 선언해 놓으면 협업 시 다른 사람이 setter 있는 거 보고 사용해도 되겠지 하고 사용하다 어떤 부작용이 생길지 모른다 들었다. 또한 setter를 남발할 시 외부에서 접근이 가능해 캡슐화 원칙이 지켜지지 않는다 한다. 이 부분은 setter 대신 생성자를 이용해 해결해야 할 것이다.

예시로는 위에 3번 항목에만 봐도 수많은 setter들이 보인다.

 

6. 데이터베이스의 양방향 통신 문제

사실 이건 완성하고나서 얼마 전에 알았는데 굳이 양방향으로 설정하지 않아도 어지간해서는 단방향으로 해결이 된다고 한다. 여태까지 엔티티 설계 시 무조건 양방향으로 해야 하는지 알았는데 잘못 알고 있었던 점이다. 이렇게 양방향으로 만들어 버리면 너무 많은 쿼리를 필요로 해 성능이 저하된다.

 

@Entity
@Table(name = "posts")
@Getter@Setter
@RequiredArgsConstructor
public class Post {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false, columnDefinition = "LONGTEXT")
    private String content;

    @ManyToOne
    @JoinColumn(name = "login_id", nullable = false)
    private User user;

    @ManyToOne
    @JoinColumn(name = "blog_id")
    private Blog blog;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "series_id")
    private Series series;

    @OneToMany(mappedBy = "post", cascade =  CascadeType.ALL, orphanRemoval = true)
    private List<Likes> likes;

    @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Comment> comments;

    private boolean temporal;

    @Column(nullable = false, name = "created_at", updatable = false)
    private LocalDateTime createdAt = LocalDateTime.now();

    @Column(name = "updated_at", nullable = true)
    private LocalDateTime updatedAt;

    @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<PostImage> postImages;


    @ManyToMany
    @JoinTable(
            name = "post_tags",
            joinColumns = @JoinColumn(name = "post_id"),
            inverseJoinColumns = @JoinColumn(name = "tag_id")
    )
    private List<Tag> tags;
}

 

@Entity
@Getter@Setter
@Table(name = "comments")
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "post_id")
    private Post post;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    private String content;
}

이런 식으로 모두 양방향 통신으로 이루어져 있다.

 

7. 테스트코드의 부재

사실 어떠한 기능을 완성하고 추가하기전에 반드시 테스트코드를 작성해 테스트를 통과한 후에 기능을 추가 해야 하는데 뭐가 그렇게 급하다고 테스트 코드도 작성안하고 부랴부랴 기능을 추가 하고 있었다. 테스트를 해도 서버를 실행시키고 직접 웹을 돌면서만 해버렸다. 테스트코드 작성도 중요하고 연습 해야 하기 때문에 다음에는 반드시 기능하나 완성시 테스트코드 작성 이런식으로 진행 할것이다.

이번 프로젝트에서는 DB를 설계하긴 했지만 명세도 제대로 안 뽑고 여러모로 설계도 부족했던 것 같고 양질의 프로젝트를 만들기보다 그냥 단순히 기능 구현에만 집중한 것 같다. 어찌어찌 기능은 작동하지만 성능적으로나 유지보수 측면에서는 정말 아쉬운 것 같다. 다음번엔 이러한 점들을 극복하고 프로젝트를 진행해야 할 것이다.

 

다음글에서는 이 기능구현에 충실하기만했던 프로젝트에서도 구현하기 까다롭고 어려웠던 부분에 대해 다뤄볼것이다.