Tree 구조, article CRUD API

calis_ws·2023년 6월 22일
0

Tree

트리란 한 개 이상의 노드로 이루어진 유한 집합이다.

  • Stack이나 Queue와 같은 자료 구조의 일종

  • 일렬적인 선형구조(Stack, Queue)가 아닌, 원소들 간 1:N1 : N 관계를 가지는 비선형 자료구조

  • 상위 원소와 하위 원소의 관계가 있는 계층적 자료구조(DOM, File System, … )

트리의 특징

  • 각각 데이터를 담고 있는 원소를 노드(Node) 또는 정점이라 한다.

  • 각 노드는 0개 이상의 자식 노드를 가질 수 있다.

  • 하나의 부모에 여러 자식이 연결되어 있다.

  • 하나의 자식은 둘 이상의 부모를 가질 수 없다.

  • 노드의 개수가 NN개일 때, N1N-1개의 간선을 가지고 있다. 그렇기에 순환 구조가 생기지 않는다.

트리의 종류

Binary Tree (이진 트리)

Perfect Binary Tree (포화 이진 트리)

Complete Binary Tree (완전 이진 트리)

Skewed Binary Tree (편향 이진 트리)

이진 트리 순회

이진 트리의 각 노드를 한번씩만 방문하는 체계적인 방법

  • 루트 노드 : V , 왼쪽 서브 트리 : L , 오른쪽 서브 트리 : R
public class TreeTraverse {
    private int nodes;
    private int[] arr;  // 이진 트리를 표현하기 위한 배열

    // { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }
    public void setArr(int[] arr) {
        this.arr = arr;
        this.nodes = arr.length;
    }

    // 전위 순회 V -> L -> R
    // preorder() : System.out.print(V) -> preorder(L) -> preorder(R)
    public void traversePreorder(int node) {
        if (node < this.nodes && arr[node] != 0) {
            System.out.print(arr[node] + ", ");         // 방문
            this.traversePreorder(node * 2);        // 왼쪽 자식(i * 2)을 루트로 다시 preorder 호출
            this.traversePreorder(node * 2 + 1);    // 오른쪽 자식(i * 2 + 1)을 루트로 다시 preorder 호출
        }
    }

    // 중위 순회 L -> V -> R
    // inorder() : preorder(L) -> System.out.print(V) -> preorder(R)
    public void traverseInorder(int node) {
        if (node < this.nodes && arr[node] != 0) {
            this.traverseInorder(node * 2);        // 왼쪽 자식(i * 2)을 루트로 다시 preorder 호출
            System.out.print(arr[node] + ", ");           // 방문
            this.traverseInorder(node * 2 + 1);    // 오른쪽 자식(i * 2 + 1)을 루트로 다시 preorder 호출
        }
    }

    // 후위 순회 L -> R -> V
    // postorder() : preorder(L) ->  preorder(R) -> System.out.print(V)
    public void traversePostorder(int node) {
        if (node < this.nodes && arr[node] != 0) {
            this.traversePostorder(node * 2);        // 왼쪽 자식(i * 2)을 루트로 다시 preorder 호출
            this.traversePostorder(node * 2 + 1);    // 오른쪽 자식(i * 2 + 1)을 루트로 다시 preorder 호출
            System.out.print(arr[node] + ", ");          // 방문
        }
    }

    public static void main(String[] args) {
        TreeTraverse tree = new TreeTraverse();
        tree.setArr(new int[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 });

        tree.traversePreorder(1);   // 처음 방문점은 root node
        System.out.println();
        tree.traverseInorder(1);
        System.out.println();
        tree.traversePostorder(1);
        System.out.println();
    }
}

article-skeleton CRUD API

직렬화(Serialization)

메모리 상 저장된 데이터를 전송 가능한 형태로 바꾸는 작업 (XML, YAML, JSON)

역직렬화(Deserialization)

직렬화 되어 있는 데이터를 메모리 상에 활용할 수 있는 형태로 바꾸는 작업

ArticleDto

package com.example.article.dto;

import com.example.article.entity.ArticleEntity;
import lombok.Data;

@Data
public class ArticleDto {
    private Long id;
    private String writer;
    private String title;
    private String content;

    public static ArticleDto fromEntity(ArticleEntity entity) {
        ArticleDto dto = new ArticleDto();
        dto.setId(entity.getId());
        dto.setWriter(entity.getWriter());
        dto.setTitle(entity.getTitle());
        dto.setContent(entity.getContent());
        return dto;
    }
}

ArticleEntity

package com.example.article.entity;

import jakarta.persistence.*;
import lombok.Data;

@Data
@Entity
@Table(name = "articles")
public class ArticleEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String writer;
    private String title;
    private String content;
}

ArticleController

package com.example.article;

import com.example.article.dto.ArticleDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.util.List;


@Slf4j
@RestController     // @ResponseBody 생략 가능
@RequiredArgsConstructor
public class ArticleController {
    private final ArticleService service;

    // POST /articles
    @PostMapping("/articles")
    // RESTful한 API는 행동의 결과로 반영된 자원의 상태를 반환함이 옳다
    public ArticleDto create(@RequestBody ArticleDto dto) {
        return service.createArticle(dto);
    }

    // GET /articles
    @GetMapping("/articles")
    public List<ArticleDto> readAll() {
        return service.readArticleAll();
    }

    // GET /articles/{id}
    @GetMapping("/articles/{id}")
    public ArticleDto read(@PathVariable("id") Long id) {
        return service.readArticle(id);
    }


    // PUT /articles/{id}
    @PutMapping("/articles/{id}")
    public ArticleDto update(@PathVariable("id") Long id, @RequestBody ArticleDto dto) {
        return service.updateArticle(id, dto);
    }


    // DELETE /articles/{id}
    @DeleteMapping("/articles/{id}")
    public void delete(@PathVariable("id") Long id) {
        service.deleteArticle(id);
    }

}

ArticleService

package com.example.article;

import com.example.article.dto.ArticleDto;
import com.example.article.entity.ArticleEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class ArticleService {
    private final ArticleRepository repository;

    public ArticleDto createArticle(ArticleDto dto) {
        ArticleEntity entity = new ArticleEntity();
        entity.setTitle(dto.getTitle());
        entity.setContent(dto.getContent());
        entity.setWriter(dto.getWriter());
        entity = repository.save(entity);
        return ArticleDto.fromEntity(entity);
    }

    public ArticleDto readArticle(Long id) {
        Optional<ArticleEntity> optionalArticle = repository.findById(id);
        // optional 안에 Article이 들어있으면
        if (optionalArticle.isPresent())
            // DTO로 전환 후 반환
            return ArticleDto.fromEntity(optionalArticle.get());
        // 아니면 404
        else throw new ResponseStatusException(HttpStatus.NOT_FOUND);

        // 위와 반대 순서
//        if (optionalArticle.isEmpty())
//            throw new ResponseStatusException(HttpStatus.NOT_FOUND);
//        return ArticleDto.fromEntity(optionalArticle.get());
    }

    public List<ArticleDto> readArticleAll() {
        List<ArticleDto> articleList = new ArrayList<>();
        for (ArticleEntity entity: repository.findAll()) {
            articleList.add(ArticleDto.fromEntity(entity));
        }
        return articleList;
    }

    public ArticleDto updateArticle(Long id, ArticleDto dto) {
        Optional<ArticleEntity> optionalArticle = repository.findById(id);
        if (optionalArticle.isPresent()) {
            ArticleEntity article = optionalArticle.get();
            article.setWriter(dto.getWriter());
            article.setTitle(dto.getTitle());
            article.setContent(dto.getContent());
            return ArticleDto.fromEntity(repository.save(article));
        }
        else throw new ResponseStatusException(HttpStatus.NOT_FOUND);
    }

    public void deleteArticle(Long id) {
        if (repository.existsById(id)) {
            repository.deleteById(id);
        }
        else throw new ResponseStatusException(HttpStatus.NOT_FOUND);
    }
}

ArticleRepository.interface

package com.example.article;

import com.example.article.entity.ArticleEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ArticleRepository extends JpaRepository<ArticleEntity, Long> {}

application.yaml

spring:
  datasource:
    url: jdbc:sqlite:db.sqlite
    driver-class-name: org.sqlite.JDBC
  jpa:
    hibernate:
      ddl-auto: create
    show-sql: true
    database-platform: org.hibernate.community.dialect.SQLiteDialect
    # 최초 실행 후 여기부터 아래까지 주석
    defer-datasource-initialization: true
  sql:
    init:
      mode: always

API TEST

POST

GET

PUT

DELETE

출처 : 멋사 5기 백엔드 위키 6팀 식스센스

인사이트 타임

백엔드 위키 작성

아기사자반

@EqualsAndHashCode

  • 중복된 데이터 골라주는 어노테이션

@NoArgsConstructor
@AllArgsConstructor

  • 접근제어자를 두어야한다. (access = AccessLevel.PROTECTED)

  • 무분별한 접근 막아준다

update, create 등 setter를 지양하는 이유는?

  • 자료변형의 가능성이 있다.

  • 메소드 이용, @builder 어노테이션 이용

JPA

  • 장점

    • 거의 쿼리 작성할 필요 없음, 메소드 사용하면 쿼리짜줌
    • DB 그리고 쿼리 이해가 부족한 주니어 개발자나 DBA가 없는 스타트업이 쓰기 좋음
    • 스키마 변경시 엔티티 수정하면 디비에 수정사항이 반영이 된다.
  • 단점

    • 조회 구림

MyBatis

  • 쿼리 작성을 해야함, 쿼리 잘짜면 최적화 굿
  • 웹 서비스 퍼포먼스 거의 DB I/O가 좌우함

review

오늘은 어김없이 배운 내용이 쉽지 않았다. 트리 구조는 들어만 봤는데 드디어 접하게 되었지만 코드를 짜라고 하면 절대 못 짤 자신있다. 알고리즘만 해도 공부할게 태산이다.

스프링은 역시나 타자연습시간. 오류없이 실행은 잘되고 코드는 대충 감만 올 뿐 정확하게 이해 못 하는건 당연지사. 진짜 큰일이다 이거야~

스프링을 시작한 후로 야생에 버려진 나는 아기사자반(보충수업 비슷)에 관심이 생겨 이번에 첫 수업을 들어보았다. 일단 멘토님 목소리가 좋다 합격. 19시부터 21시까지 수업하는데 쉬는 시간이 없지만 줌을 키지 않아도 되는게 좋은 것 같다.
수업 내용은 스프링에 관한 멘토님의 꿀팁 몇 가지 얻어가는 정도? 기대한 것에 비해 살짝 아쉬웠지만 그래도 별점 5점중에 3.5점 정도로 괜찮았던 것 같다. 수업 분위기가 무겁지 않아서 그런지 편하게 들을 수 있다는 것이 제일 좋았다. 다음에도 어떤 꿀팁을 건질지도 모르니 한 번 더 들어봐야겠다.

profile
반갑습니다람지

0개의 댓글