spring.h2.console.enabled=true

=>h2 콘솔 볼수 있게 해줘

 

spring.datasource.url=jdbc:h2:mem:testdb

=> 스프링의 데이터 소스(데이터베이스)를 h2로 할꺼야 라는 의미

 

http://localhost:80 의 의미

 

localhost는 내 컴퓨터를 의미하고 여기에는 무수히 많은 방이 있는데 그중 80번째 방에 스프링 서버를 띄워 놨어 라는 의미이다.

 

 

localhost:80/h2-console 접속 후

 Connect 버튼 누르기

 

 

h2 웹 콘솔 띄우면서 에러 발생

 

 'C:\Users\사용자명' 폴더 안에 test파일이 없어서 발생한 문제이다.

=> 'C:\Users\사용자명' 안에 빈 텍스트 파일을 생성하고

     파일명을 확장자를 포함한 'test.mv.db'로 바꾸고 연결을 재시도한다.

 

 

CREATE TABLE IF NOT EXISTS courses (
    id bigint NOT NULL AUTO_INCREMENT, 
    title varchar(255) NOT NULL,
    tutor varchar(255) NOT NULL,
    PRIMARY KEY (id)
);

 

h2DataBase에서

bigint는 크기 지정해 주면  SystaxError 뜬다.

 

데이터를 넣을때  title,tutor라는 문자열을 넣어주고 id는 자동으로 증가시켜줘 라는 의미이다.

단 title,tutor는 문자열이다 라는 의미이다.

 

 

@Id 
@GeneratedValue(strategy = GenerationType.AUTO) // 자동 증가 명령입니다.
private Long id;

@Id 의미 : PrimaryKeY로 사용하겠다는 뜻이다.

strategy = GenerationType.Auto 의 의미:   생성전략을 지칭하는 말로  DB에서 AUTO_INCREMENT  의 의미이다.

 

 

@Column(nullable = false) 
private String title;

@Column은 테이블에서 컬럼을 의미힌다.(하나의 열)

nullable는 null을 가능하게 할것인지 말것인지의 의미(NOT NULL  기능을 할 수 있게 해주는 것이다.)

 

 

 

Id 는 DB에서 사용하는 것이기 때문에 id에서는 Getter와 Setter를 만들어줄 필요는 없다.

 

나머지 필드에서 Getter만 설정해 주면된다.

 

Setter 를 설정해 주지 않는 이유 => Repository에서 자동으로 Setter를 설정해줌

그래서 Getter 만 설정해 주면된다.

 

 

상속 : 다른녀석의 기능(뒤에 적혀있는 놈의)을 내가 가져와서 쓸꺼야....라는 의미

 

 

public interface CourseRepository extends JpaRepository<Course, Long> {
}

JPA는 SQL쿼리 날리는것인데 어떤걸 대상으로 어떤걸 식별할 것인지를 적어줘야한다.

<Course,Long>    이 이런 역할을 한다.

 

Course라는 녀석이거 Id의 형태가 Long이다 라고 적어줘야한다.

 

 

 

웹 콘솔 접속해서 확인할떄 

application.properties에 적은 부분을 확인해 줘야한다.

spring.datasource.url=jdbc:h2:mem:testdb

아래 사진의  JDBC URL에 application.properties에 적혀있는게 들어가 있어야 JPA로 데이터 삽입하고 웹 콘솔로 확인이 가능하다.

 

 

//JPA실행 부분 여기 코드를 아는 것이 중요하다.
Course course1 = new Course("웹개발","홍킬동");
repository.save(course1);   //Insert INTO의 역할

List<Course> courseList = repository.findAll();
for (int i = 0; i < courseList.size(); i++) {
    Course c = courseList.get(i);
    System.out.println(c.getTitle());
    System.out.println(c.getTutor());
}

JPA 코드를 반복 숙달하는 것이 중요하다.

 

 

어노테이션의 역할

스프링한테 야 이게 이런 역할이 있어 라고 알려주는것이다.

 

 

 

 

@EntityListeners(AuditingEntityListener.class)

Entity는 테이블을 의미한다.

Listeners 주시해라

 

Auditing 수정해라

 

테이블을 주시하다가 수정사항이 있으면 자동으로 반영 해줘 라는 의미이다.

(여기서는 modifiedAt이 계속 수정되므로 수정하고 반영하라고 스프링에게 알려주는것이다.)

 

@MappedSuperclass

이 클래스를 상속하면 멤버 변수가 있을텐데 그것도 컬럼으로 인식해줘라

라는 의미.

 

abstract : new 이런식으로 생성못하고 상속해서만 쓸수 있다는 의미로 간단하게 생각 가능

 

 

 

 

 

Course course = repository.findById(1L).orElseThrow(
        () -> new IllegalArgumentException("해당 아이디가 존재하지 않습니다.")
);

orElseThrow : 만약에 찾는 아이디가 없으면 throw(이렇게~)해라 라는 의미이다.

IllegalArgumentException : 부적절한 인수가 왔을때 Exception(대처방법)   "해당 아이디가 존재하지 않습니다." 메세지를 띄워라.

 

 

컨트롤러는 요청을 받아주는 자동응답기

레포지토리는 DB에서 데이터를 꺼내오는 역할

두개를 이어주는 역할이 필요하다.

그것이 바로 Service

 

 

 

 

 

CourseService.java 분석

@Service

스프링한테 이거는 서비스라고 알려준다.

update하는 작용이 일어날수 있으니까 알아둬~ 라고 스프링한테 알려주는거다.

 

private final CourseRepository courseRepository;

CourseRepository가 꼭 필요하다. 왜냐하면 검색을 한다던가 업데이트를 한다거나 할떄 Repository가 필요하기 떄문이다.(SQL을 날리는 녀석이기 때문..)

 

final  : 이거 이 클래스에 꼭 필요한거야!(final은 한번 값을 부여하면 변형되지 않는다.)

 

public CourseService(CourseRepository courseRepository) {
    this.courseRepository = courseRepository;
}

여기서 CourseRepository가 매개변수로 들어오는 생성자를 선언하는데.

CourseRepository 누가 만들어서 매개변수로 넣어주냐? => 스프링에서 알아서 넣어줌

CourseService를 필요할때 생성해 주는게 스프링 이거든~ 

@Service라는 마크를 달아줌으로써 생성할때 알아서 잘 넣어준다.

final이라는게 있기때문에 스프링이 Repositroy에 스윽 잘 넣어준다.

 

private final CourseRepository courseRepository;

public CourseService(CourseRepository courseRepository) {
    this.courseRepository = courseRepository;
}

이 두줄을 통해서 CourseRepository가 내가 언제든 쓸 수 있게 스프링이 생성해서 넘겨준다.

 

 

public Long update(Long id, Course course) {

처음 Long : 업데이트 하고나면 어떤 녀석이 업데이트 됐는지 알려주는 부분

 

그리고 전달 받는 거는 두개의 재료가 필요하다.

첫번째로 업데이트를 할떄 어떤 녀석이 업데이트를 해야하는지 가르쳐 줘야 하잖아!

그래서 ID가 필요

두번째로 업데이트할 정보를 가져올 녀석이 필요(=>여기서는 Course 클래스 이용)

 

@Transactional 
public Long update(Long id, Course course) {
    Course course1 = courseRepository.findById(id).orElseThrow(
            () -> new IllegalArgumentException("해당 아이디가 존재하지 않습니다.")
    );
    course1.update(course);
    return course1.getId();
}

@Transactional 의미 : SQL쿼리가 일어나야 함을 스프링에게 알려줌

 

첫번째로 해야할 일은 Repository를 이용하여(ID이용해서) 업데이트할 녀석을 찾고 없으면

"해당 아이디가 존재하지 않습니다" 출력

 

아이디에 해당하는 녀석이

만약에 1이라면 1에 해당하는 정보가 course1에 들어가 있을것이다.

만약에 2라면 2에 해당하는 정보가 course1에 들어가 있을것이다.

 

course1이 업데이트 되도록 메서드를 설정하면

우리가 전달받은 이 코스(매개변수로 들어온 course) 정보가 파라미터 즉 정보로 넘어간다.

그래서 업데이트가 되고

그 다음에 return 업데이트한 course1의 아이디를 돌려줘라   라고 코드를 짠거다/

 

 

Course라는 클래스 안에 update메서드를 생성한 이유가 여기서 나온다.

public void update(Course course) {
    this.title = course.title;
    this.tutor = course.tutor;
}

내가 변경할 정보를 가져와서(>course를 매개변수로 받아오기 때문<)

내 정보를 이렇게 바꿔 준다.....

 

그러면 그 정보가 자동으로 반영이 된다.

왜냐?

CourseService안에 있는

@Transactional  어노테이션을 가진 코딩 때문에 자동으로 데이터 베이스에 적용된다.

 

 

 

@Bean
public CommandLineRunner demo(CourseRepository courseRepository, CourseService courseService) {
    return (args) -> {
        courseRepository.save(new Course("프론트엔드 개발 수업", "홍킬동"));

        System.out.println("데이터 인쇄");
        List<Course> courseList = courseRepository.findAll();
        for (int i=0; i<courseList.size(); i++) {
            Course course = courseList.get(i);
            System.out.println(course.getId());
            System.out.println(course.getTitle());
            System.out.println(course.getTutor());
        }

        Course new_course = new Course("백엔드 개발 수업", "홍킬동");
        courseService.update(1L, new_course);
        courseList = courseRepository.findAll();
        for (int i=0; i<courseList.size(); i++) {
            Course course = courseList.get(i);
            System.out.println(course.getId());
            System.out.println(course.getTitle());
            System.out.println(course.getTutor());
        }
    };
}

위 코드를 분석해 보자

첫번째로 저장을 한다.(프론트엔드 개발 수업, 홍킬동)

그거한번 찾아서 쭉 인쇄한다.

 

그 다음에 

백엔드 개발 수업, 홍킬동

튜터이름은 똑같고 강의 제목만 바뀐 형태로 new Course를 하나 만든다.

이 용도는 저장 하는게 아니라 기존의 데이터를 변경하는 형태로 쓰인다. 

 

courseService의 update 기능을 활용한다.

우리가 바꿀 녀석은 아이디가 1인 녀석이다.

1인 녀석을 가지고 변경할 Course를 넘겨준다.

 

그 다음에 전체 검색을 해서 제대로 바뀌었는지 확인하는 코드이다.

 

 

LOMBOK과 DTO 

 

LOMBOK을 사용하면

Getter 부분이나 생성자 부분에서 코드를 간결하게 사용할 수 있다(줄일 수 있다.).

 

DTO : 데이터를 주고 받을때는 새로운 클래스 사용하자. 기존의 클래스 사용하지 말자 이런 뜻이다.

(데이터를 몰고 다닐떄 사용   Service -> Repository로 갈때 또는 Controller ->Service 또는 Controller  -> Repository 로 갈떄)

 

courseService의

@Transactional 
public Long update(Long id, Course course) {

이 부분에서 업데이트할 내용을 가진 정보를 넘겨 줄때 Course 클래스를 이용하는게 맞는건가?

 

Course 클래스를 이용할떄는 직접 저장할때나 찾아서 클라이언트를 넘겨줄떄나 활용하는 것이다.

변경을 할때

***변경을 할 뗴이터를 가지고 다니는 매개체로 Course 클래스를 사용하는건 안좋다.

Course 클래스를 건드리는건 DB가 변경될 가능성이 있다...그래서 함부로 건들면 안된다.

DB에 연결된 클래스는 그대로 두고 정보를 들고다니는 클래스의 필요성이 느껴져

생기는게 DTO이다.

 

 

private으로 선언한 녀석이 final로 선언되면 그 필요한 생성자도 바로 만들어 줘야한다.

생성자를 안만들어주면 오류난다.

왜?

없을 수도 있기 때문이다.

따라서, 아래와 같은 코드를 선언해 준다.

@RequiredArgsConstructor

 

 

 

API

 

@RequiredArgsConstructor
@RestController
public class CourseController {

    private final CourseRepository courseRepository;

    @GetMapping("/api/courses")
    public List<Course> getCourses() {
        return courseRepository.findAll();
    }
}

위 코드를 분석해 보자.

Get 방식에 의해서 요청을 받는다.("/api/courses")

데이터를 조회 하기 위해서

return 값에 courseRepository.findAll()를 활용한다.

 

courseRepository를 사용하기 위해서는 필드명에 선언을 해줘야하는데

private이고 final로 선언했기 떄문에

@RequiredArgsConstructor가 필요하다.

 

 

API를 만들고 나면 실제로 동작하는지 확인하는 방법

2가지

첫번째 : 테스트 코드를 작성

두번쨰 : 툴을 사용해서 하는 방법       (ARC 이용)

 

 

POST

 

@RequestBody 의미 : 컨틀롤러에서 요청을 받는 녀석이다 라는 것을 표시해 주기 위해 주는 어노테이션이다.

@RequestBody가 없으면 요청한 정보가 RequestDto에 쏙 안들어간다.

(초반에 스프링은 요청을 주고받는 방식을 강제 한다고 한 의미가 이런것이다.)

POST 나 PUT에서 데이터를 주고 받을 때 API에서 넘어오는 데이터를 잘 받으려면  @RequestBody 형태로 받아줘야한다.

 

POST 는 요청하는 방식이 정해져 있다.

왜냐하면 어떤 데이터를 만들건지 일단 건네줘야 한다.

그 건네는 방식이 정해져 있다.

 

왜냐면 Spring Post 방식에서 데이터를 전달하는 방식은 엄격하게 정해져 있다.

(이해보다는 사실 암기 => 스프링 개발자들이 정해놓은 것이다.)

 

 

 

왜 하필 json 방식일까?

=> json형식이 인터넷에서 왔다갔다하는 되게 대중적인 방식이기 때문이다.

다른방식도 존재하지만

Get방식으로 데이터를 전달 받을 때도 json형식으로 받은 다음에 브라우저에서 봤잖아!!!!!

마찬가지로 요청을 전달할때도 GET 방식으로 전달해 주는게 합리적이지 않을까?!!

 

 

@PutMapping("/api/courses/{id}")
public Long updateCourse(@PathVariable Long id, @RequestBody CourseRequestDto requestDto) {
    return courseService.update(id, requestDto);
}

{id} 의 의미:

/api/courses/12

/api/courses/25

/api/courses/35 와 같이 id값은 유동적으로 바뀌는 부분이다.

이때는 이런식으로 주소에서 중괄호로 감싸주고 변수명을 적어준다.

(변형되는 값이 들어오는구나 라는걸 Spring에게 알려주기 위해서 중괄호 사용)

 

파라미터로는 2가지가 들어온다.

첫번째 변경하고 싶은 id 값이 뭐냐

두번째 변경할 내용이 뭐냐

 

@PathVariable의 의미 : Lond id가  하늘에서 뚝 떨어진게 아니라 ,  주소 에서 중괄호로 감싸진 부분을 가져오겠다는 의미

 

 

 

 

cf) 서버 재시작 했는데 아까 나 분명 2개 생성했는데 왜 1개밖에 없음...?

이런 의문 가지면 안된다....멍청이!

 

왜냐하면 우리는 지금 h2데이터베이스를 쓰고 있는데

h2데이터베이스는 서버가 한번 내려갔다가 올라오면
기존 데이터를 전부 다 삭제해 버리기 떄문이다.

 

총 복습

Controller : 자동응답기

Service : update쓰는 녀석

Repository : 직접 쿼리를 날리는 녀석

 

 

 

 

복사했습니다!