티스토리 뷰

CRUD란?

서비스의 자원

웹 서비스(어플리케이션 서비스)

  • 사용자가 원하는(서비스에서 사용할) 자원의 관리
  • 자원 제공, 제작, 갱신 등
  • 부수적인 기능은 서비스 별로 다르게 만들어짐

CRUD (Create, Read, Update, Delete)

Create 생성

  • DTO(Data Transfer Object): 데이터를 주고받는데 사용하는 객체라는 의미
@Controller
@ResponseBody
@RequestMapping("post")
public class PostController(){
	private final List<PostDto> postList;
    
	public PostController(){
		postList = new ArrayList<>();
	}
    
	@PostMapping("create")
	public void createPost(@RequestBody PostDto postDto){
		this.postlist.add(postDto);
}
  • @Controller와 @ResponseBody 어노테이션이 클래스 위에 둘 다 붙어있으면, RestController가 Controller에 있는 모든 함수에 ResponseBody를 붙여넣는것과 같은 것처럼, 클래스 안의 모든 함수들이 ResponseBody가 붙어있는 형태로 함수 선언이 완료됨
  • @RequestMapping 어노테이션이 클래스 위에 붙으면, 클래스 내용물의 requestmapping에 따라 앞쪽에 모두 "post"가 붙음
  • postList가 초기화되지 않은 상태이면, final로 선언 시 오류 발생 > constructor에서 선언

Read 조회

@GetMapping("read-all")
public List<PostDto> readPostAll(){
	return this.postList;
}

@GetMapping("read-one")
public PostDto readPostOne(@RequestParam("id") int id){
	return this.postList.get(id);
}

@RequestParam

@RequestParam
- used to get request parameters from URL
- also known as query parameters
- extract values from the query string
- encoded
@PathVariable
- extracts values from URI
- not encoded
http://localhost:8080/shop/order/1001/receipts?date=12-05-2017​
@GetMapping(value = "/order/{orderId}/receipts")
public List listUsersInvoices(@PathVariable("orderId") int orderId,
			@RequestParam(value = "date", required = false) Date dateOrNull) {}​

 

encoded / not encoded 차이점
http://localhost:8080/spring-mvc-basics/member/ab+c
// using @PathVariable, id = ab+c

http://localhost:8080/spring-mvc-basics/member?id=ab+c
// using @RequestParameter, id = ab c​

@PathVariable의 경우 not encoded 이기 때문에 ab+c 그대로 아이디값으로 적용
@RequestParam의 경우 url의 parameter가 decode되기 때문에 id값은 ab c가 됨

@PostMapping("update")
public void updatePost(@RequestParam("id") int id, @RequestBody PostDto postDto){
	PostDto targetPost = this.postList.get(id); //update를 하기 위한 목적 게시글
    if (postDto.getTitle() != null){
		targetPost.setTitle(postDto.getTitle());
	}
    if (postDto.getContent() != null){
		targetPost.setContent(postDto.getContent());
	}
	this.postList.set(id, targetPost);
}

Update 갱신

@DeleteMapping("delete")
public void deletePost(@RequestParam("id") int id){
	this.postList.remove(id);
}

Delete 삭제


CRUD와 REST

CRUD와 REST는 아무 관계가 없음. 어떻게 CRUD를 RESTful하게 만들 것인가?

RESTful이란?

REST(REpresentational State Transfer)

  • 서버의 형태에 따라서 클라이언트 형태가 고정되어 있을 경우 coupling/결합성이 높다 고 표현
  • RESTful: Client와 Server간의 결합성을 줄이기 위한 가이드라인
    • 결합성이 줄어듦으로써 성능이 좋아지고, 확장성이 뛰어나지고, 사용이 더 간편해짐
    • 실행되는 와중에도 기능의 변화가 일어날 수 있음
    • 특정 기술을 쓴다 는 의미가 아님
    • 클라이언트가 사용할 API를 누가 사용해도 문제 없게 만드는 과정
    • 꼭 HTTP만을 사용하는 것이 아님
API (Application Programming Interface)
- software intermediary that allows two applications to talk to each other.
- an API is the messenger that takes the request and tells the system what the user want to do and then returns the response back to the user.
- a tool set that programmers can use in helping them create software.
- 라이브러리: 함수들의 집합
- API: 라이브러리에 접근하기 위한 규칙들을 정의한 것
- 라이브러리가 제공하는 여러 함수를 이용하여 프로그램을 작성할 때 해당 함수의 내부 구조는 알 필요 없이 단순히 API에 정의된 입력 값을 주고 결과 값을 사용할 수 있게 해줌.

OPEN API
- 특정 서비스를 제공하는 서비스 업체가 자신들의 서비스에 접근할 수 있도록 그 방법을 외부에 공개한 것으로 해당 서비스로 접근하기 위한 규칙을 정의한 것. ex) 네이버 아이디로 로그인하기
  • 몇가지 제약사항이 있으며, 이 제약사항들을 최대한 잘 지키는 것이 RESTful한 API
    • Clint Server Architecture: 클라이언트와 서버가 얼마나 잘 분리되어 있는가. 역할이 잘 분담되어 있는지.
    • Statelessness: 상태를 가지지 않는다. API자체가 상태를 저장해서 그것을 기반으로 만드는 과정을 포함하지 않기.
    • Cacheablity: 재사용 가능한 결과인지.
    • Layered System: 실제 서버까지 도달하는 과정을 클라이언트가 알 필요가 없다
    • Uniformed Interface
    • Code on Demand (Optional): application이 실행 중인 상태에서도 내용/기능을 변화시킬 수 있는지
    • 서비스가 커지면 REST의 조건을 100% 만족하기 어려움

Client Server Architecture (Client Server Model)

  • 서버와 클라이언트의 분리
  • separation of concern 개념과 관계
  • 서버와 클라이언트의 동작 방식에 대해 분리해서 생각해야 함
  • 서버에서 자원을 관리하고, 자원에 대한 표현은 클라이언트에서만 관여
  • 서로의 변화가 서로에게 영향을 주지 않는 형태가 되어야 한다 (새로운 기능이 추가된 것이 아닌 이상)
    • 클라이언트가 변해도 서버가 변할 필요가 없고, 서버가 변해도 클라이언트가 변할 필요가 없음
    • ex) server가 Spring Boot 2.2에서 2.5로 버전업을 해도 client에 영향을 주지 않음
    • endpoint는 그대로 있고, 내부에 있는 코드만 바뀜
    • endpoint 어떤 식으로 요청을 보내는가 등 API의 모습은 그대로 남아 있음
    • RequestMapping을 변경시키지는 않는다

Statelessness

  • Request를 처리하는데 필요한 모든 정보는 해당 Request에 포함되어 있어야 함
  • 서버가 요청을 받을 때마다 사용자가 누구였는지를 매번 다시 확인함
  • 서버 안에 사용자 정보를 기록하지 않는다 (모든 요청은 서로 독립적)
  • 서버는 Request를 처리하는데 필요한 클라이언트의 그 어떤 컨텍스트도 서버의 세션에 담지 않음
  • 상태를 저장하지 않음
  • 원하는 기능을 위한 상태는 Client가 가지고 있어야 한다
  • ex) 로그인 된 상태에서의 API와 로그인되지 않은 상태의 API는 똑같은 endpoint를 가지고 있음. API에 요청이 들어왔을 때 어떤 사용자인지를 증명하는 것은 API의 사용자측에서 증명을 해야 함

Cacheablility

  • cache: 가져왔을 때 가져온 것에 대한 기능을 클라이언트의 컴퓨터에 저장하는 기능 (?)
  • Request에 대한 Response는 저장 가능 여부(클라이언트가 동일한 요청을 다시 하지 않고 재사용할 수 있도록) 및 기간을 표시해야 함
  • a cacheable response can be stored to be retrieved and used later
  • 자원의 캐싱이 가능한지의 여부를 항상 표기해줘야 한다

Layered System

  • 계층 구조
  • 클라이언트가 서버와 상호작용을 할 때, 오직 서버만을 알고 있어야 함
  • 서버를 이루는 인프라스트럭쳐는 계속 뒷단에 숨겨져 있어야 하고, 클라이언트는 오직 시스템에서 하나의 레이어만 볼 수 있어야 함
  • 클라이언트는 서버에 도달하기까지의 과정을 알 필요가 없다
  • 서버가 하나가 아닐 때, 클라이언트가 요청이 어느 서버로 가야 하는지를 알아야 한다면 좋은 구조가 아님. 어느 서버로 가도 동일한 응답을 주는 것이 좋은 구조

Uniformed Interface

  • 일관된 인터페이스
  • 어떤 자원에 대한 요청을 보내는 것인지가 명백하게 드러나야 하고, 서버에서 응답을 돌려줄 때 그 자원 자체가 아닌 그 자원의 형태를 띈 데이터를 돌려주는 것
  • 모든 상호작용은 식별된 리소스의 개념에 따라 이루어져야 하는데, 이는 식별된 리소스의 조작은 리소스의 상태 표현들과 표준 메소드들을 통해서만 이루어짐을 의미함
  • 상호작용은 리소스의 표현이 무엇을 의미하는지와 이 리소스들로 무엇을 할 수 있는지 알려줄 수 있는 모든 메타 데이터를 제공해야 함

Code on Demand

  • server-side scripting
  • 일시적 기능의 확장
  • 사용 가능한 코드를 응답으로 보내 사용자의 기능을 일시적으로 확장시킬 수 있다
  • 서버는 필요하다면 클라이언트에 실행 가능한 코드를 전송할 수 있어야 함
  • 선택사항으로 무조건 충족해야 하는 것은 아님

API를 RESTful하게 설계하는 방법 (CRUD의 재구성)

path(경로)를 기능(get, create, read, ...)이 아닌 자원의 위치를 나타내는 데 사용. 기능은 HTTP 요청의 method에서 정의

  • 경로(path)를 통해 도달하고자 하는 지원을 지정
  • 방법(method)을 통해 자원에 실행할 기능을 지정

RequestMapping 재구성

@DeleteMapping("delete")
public void deletePost(@RequestParam("id") int id){
	this.postList.remove(id);
}
  • 위 예시와 같이 "delete"(URL)에 기능을 추가한 형태는 좋은 형태로 보기 어려움.
  • URL: 자원이 어디에 있는지를 나타내기 위해 사용하는 것

PostController에서 사용자가 볼 수 있는 화면을 제공하는 RequestMapping, GetMapping을 만들고, RestController에서 실제로 화면을 사용하기 위한 내용, API들을 만들어주는 형태로 만들 수 있음 (두 가지 컨트롤러를 만들어 두고 둘 다 사용 가능)

@RestController
@RequestMapping("post")
public class PostRestController {
    private static final Logger logger = LoggerFactory.getLogger(PostRestController.class);
    private final List<PostDto> postList;

    public PostRestController(){
        this.postList = new ArrayList<>();
    }

    // http://localhost:8080/post
    // POST /post
    // REQUEST_BODY

    @PostMapping()
    @ResponseStatus(HttpStatus.CREATED)
    public void createPost(@RequestBody PostDto postDto){
        logger.info(postDto.toString());
        this.postList.add(postDto);
    }

    // http://localhost:8080/post
    // GET /post

    @GetMapping()
    public List<PostDto> readPostAll(){
        logger.info("in read post all");
        return this.postList;
    }

    // GET /post/0
    // GET /post?id=0

    @GetMapping("{id}")
    public PostDto readPost(@PathVariable("id") int id){
        logger.info("in read post");
        return this.postList.get(id);
    }

    // http://localhost:8080/post
    // put /post/0
    // 현재 보내는 데이터를 그 위치에 다시 넣어달라 (대체)

    @PutMapping("{id}")
    public void updatePost(
            @PathVariable("id") int id,
            @RequestBody PostDto postDto){
        logger.info("target id: " + id);
        PostDto targetPost = this.postList.get(id);
        if (postDto.getContent != null) {
            targetPost.setContent(postDto.getContent());
        }
        this.postList.set(id, targetPost);
    }

    // DELETE /post/0

    @DeleteMapping("{id}")
    public void deletePost(@PathVariable("id") int id){
        this.postList.remove(id);
    }
}

 

  • @RequestBody: body에 (새로운)데이터를 전달해야 할 때 사용
  • @ResponseStatus(): 200 OK 외에 다른 여러가지 status code로 지정 가능
  • 경로, 특정한 자원의 조건을 주면서 하고 싶을 때는 query, requestparameter를 사용하는 것이 HTTP API에서 일관성 있는 인터페이스임
  • 특정한 자원에 대한 요청을 하고 싶을 때에는 경로에 자원의 아이디를 제공하면 됨
  • 자원에서 어떤 작업을 하고 싶은지는 메소드로 표현
  • 메타 데이터와 같은 정보를 전달하면 해당 정보에 대한 자원을 생성하고, 이미 존재하는 자원에 대한 표현을 돌려줌. 실제 자원은 바이트(데이터) 형태로 데이터베이스에 들어가 있으며, 돌아온 결과는 JSON 형태임.

Spring Stereotypes

Spring IoC Container가 만들어둔 코드에서 bean을 검색해서 필요한 순간순간에 제공하는 것이 Spring Framework의 핵심적인 기능 중 하나

bean을 method 단위로 정의할 수도 있고, class 단위로 정의할 수도 있음

class 단위로 정의하기 위한 것들이 정의되어 있는 (구현되어 있는) 패키지 이름이 Spring Framework Stereotype

Component란?

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}

@SpringBootApplication

  • SpringBootApplication 어노테이션 인터페이스
  • 이 어노테이션이 붙어있으면 메인 클래스가 SpringBootApplication으로 실행됨
  • @SpringBootConfiguration, @EnableAutoConfiguration: 자동으로 SpringBoot에 내장되어 있는 설정을 가져다가 쓴다는 의미의 어노테이션
  • @ComponentScan
    • @Configuration 어노테이션과 함께 활용한다면 어떤 패키지들을 확인해서 IoC Container에 등록할 것인지 정의하기 위해 사용
    • excludeFilters{@Filter(type,classes)}를 사용하여 필터할 것들을 정의할 수 있음.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";

}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 * @since 4.0.1
	 */
	@AliasFor(annotation = Controller.class)
	String value() default "";

}

Component

  • @Controller는 @Component 인터페이스의 일종임
  • @Component에는 @Controller뿐만 아니라 @Service @Repository 등도 존재함
  • @Component을 활용해 커스텀 컴포넌트 인터페이스를 만들어 사용할 수도 있음 ex) SpringBoot GRPC starter package
  • @Component가 붙어있는 클래스들을 검색해서(@Controller, @Service, @Repository 등 하위 인터페이스 어노테이션 포함) Spring Container에 등록해준다
  • Spring IoC Container는 @ComponentScan을 이용해 사용할 Bean의 범위를 정해줄 수 있다
    • 함수 단위: @Bean  클래스 단위: @Component 사용
  • 모든 Bean에 (@Controller, @Repository, @Service 대신) @Component를 사용해도 작동하기는 한다
  • @Controller: RequestMapping과 함께 사용. MVC의 Controller 역할을 함을 알림
  • @Repository: Data Access Object와 같이 실제 데이터 근원과 소통하는 부분임을 알림
  • @Service: 비즈니스 로직이 구현된 부분임을 알림

Service, Repository 사용하기

public interface PostRepository {
    boolean save(PostDto dto);
    List<PostDto> findAll();
    PostDto findById(int id);
    boolean update(int id, PostDto dto);
    boolean delete(int id);
}
@Repository
public class PostRepositoryInMemory implements PostRepository{
    private static final Logger logger = LoggerFactory.getLogger(PostRepositoryInMemory.class);
    private final List<PostDto> postList;

    public PostRepositoryInMemory() {
        this.postList = new ArrayList<>();
    }

    @Override
    public boolean save(PostDto dto) {
        return this.postList.add(dto);
    }

    @Override
    public List<PostDto> findAll() {
        return this.postList;
    }

    @Override
    public PostDto findById(int id) {
        return this.postList.get(id);
    }

    @Override
    public boolean update(int id, PostDto postDto) {
        PostDto targetPost = this.postList.get(id);
        if (postDto.getTitle() != null){
            targetPost.setTitle(postDto.getTitle());
        }
        if (postDto.getContent() != null){
            targetPost.setContent(postDto.getContent());
        }
        this.postList.set(id, targetPost);
        return true;
    }

    @Override
    public boolean delete(int id) {
        this.postList.remove(id);
        return true;
    }
}

인터페이스를 요구해도 해당 인터페이스의 구현체 클래스를 가져다줌 (구현체클래스간의 우선순위는 존재함)

@Deprecated: 당장 사용할 수는 있으나(지원되나), 곧 deprecated 될 것이다 (더 이상 사용하지 않을 예정이다)

@Controller
@ResponseBody
//@RequestMapping(value = "post")
public class PostController {
    private static final Logger logger = LoggerFactory.getLogger(PostController.class);
    private final PostService postService;

    public PostController(
            @Autowired PostService postService
    ){
        this.postService = postService;
    }

    @PostMapping("create")
    public void createPost(@RequestBody PostDto postDto){
        logger.info(postDto.toString());
        this.postService.createPost(postDto);
    }
  • @Autowired: 해당 인터페이스를 만족하는 클래스를 IoC Container에서 제공해서 인자로 자동으로 주입(Dependency Injection)

예시에서의 @Controller: 요청을 받고 응답을 보내는 역할로써만 사용, 이외에는 @RestController 사용

@SpringBootApplication
public class CrudApplication {

	public static void main(String[] args) {
		SpringApplication.run(CrudApplication.class, args);
	}

}

메인 함수에서 실제로 PostController를 실제로 선언하지 않았음에도 불구하고 PostController에 포함된 함수들은 모두 등록이 되어 있음. PostController 클래스가 어느 시점에서 자동으로 생성이 되었었다는 것을 의미함.

@Service, @Repository 같은 어노테이션은 관리적인 측면에서 사용하는 부분이 더 큼. 반드시 사용해야 하는 것은 아님.

 

 @PostMapping()
    @ResponseStatus(HttpStatus.CREATED)
    public void createPost(@RequestBody PostDto postDto, HttpServletRequest request){
        logger.info(postDto.toString());
        request.getHeader("Content-Type");
        this.postService.createPost(postDto);
    }

HttpServletRequest

  • 자바 상에서 구현되어있는 http interface
  • 함수의 인자로 넣어줄 수 있음
  • embedded tomcat이나 다른 구현체에서 만들어놓은 요청이 들어올 수 있고, 내용을 확인할 수 있음 (request.getHeader~)
    @PostMapping()
    public ResponseEntity<BoardDto> createBoard(@RequestBody BoardDto boardDto){
        return ResponseEntity.ok(boardService.createBoard(boardDto));
    }

@ResponseEntity

  • http 응답을 만들 수 있는 다양한 함수가 저장되어 있음 (응답을 좀더 잘 조작해서 보내고 싶을 때 사용)
  • .status를 활용해 status code를 직접 넣어줄 수도 있음
  • 여러가지 builder 함수들이 있음
	/**
	 * Create a builder with the given status.
	 * @param status the response status
	 * @return the created builder
	 * @since 4.1
	 */
	public static BodyBuilder status(int status) {
		return new DefaultBuilder(status);
	}

	/**
	 * Create a builder with the status set to {@linkplain HttpStatus#OK OK}.
	 * @return the created builder
	 * @since 4.1
	 */
	public static BodyBuilder ok() {
		return status(HttpStatus.OK);
	}

 

@Autowired 등 어노테이션 정의 찾아보기


Database 다뤄보기

관계형 데이터베이스와 ERD

관계형 데이터베이스 (Relation Database): Codd의 12규칙을 따르고자 하는 Database (전부 다 따르지는 않음)

  • Table(Relation)의 형태로 데이터를 저장
  • 관계형 연산자로 테이블 형태로 데이터를 반환
  • Row: entity를 담고 있음
  • Column: 역할을 담고 있음
  • Primary Key: primary key값이 있다면 하나의 entity(row)를 명백하게 가져올 수 있는 값

ERD (Entity-Relationship Diagram): Entity와 Entity 사이에 어떤 관계를 가지고 있는지 나타내기 위한 모델

  •  

MySQL과 Workbench 설치하기

관계형 데이터베이스 서버: 서버 소프트웨어(Docker 등), 물리 서버

서버에 접속하기 위한 클라이언트: MySQL워크벤치 등

127.0.0.1: 자기 컴퓨터를 지칭하는 ip

 

 

MySQL

PK (Primary Key)

NN (Not Null)

AI (Auto Increment): 데이터가 하나 추가될 때 id값이 자동으로 +1

 

command + enter: 선택한 부분만 실행

 

inner join, outer join 찾아보기

 

truncate post: table이 한 번도 사용된 적 없는 상태로 되돌리는 것 (delete와는 다름). id도 초기화 됨 (delete 후 생성하면 id는 auto-increment 되어있음)

'Java > project lion JSB the origin' 카테고리의 다른 글

Ch.6 Spring Boot 기능활용(1)  (0) 2022.03.04
Ch.5 CRUD & Data (2)  (0) 2022.03.01
Ch.3 Spring Boot Basics (2)  (0) 2022.02.13
Ch.2 Spring Boot Basics(1)  (0) 2022.02.05
Ch.1 Basics  (0) 2022.01.29
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함