프로토콜
- 메시지 송/수신자가 서로 필요한 요청과 응답할 수 있도록 미리 규약해 놓은 것
HTTP (HyperText Transfer Protocol)
- 데이터 주고 받는 양식을 정의한 통신 규약 중 하나
- 브라우저가 Request(요청)하면, 서버가 Response(응답)
naver.com 에 접속하면 보여지는 화면에 대한 데이터는 브라우저가 서버에서 사이트 정보를 받아와 표현 하는것
API(application programming interface)
- 다른 소프트웨어 시스템과 통신하기 위해 따라야 하는 규칙
- 웹 API는 클라이언트와 웹 리소스 사이의 게이트웨이
인터페이스(interface)
- 서로 다른 두 개의 시스템, 장치 사이에서 정보나 신호를 주고받는 경우의 접점이나 경계면
- 즉, 사용자가 기기를 쉽게 동작시키는데 도움을 주는 시스템
RESTful API
두 컴퓨터 시스템이 인터넷을 통해 정보를 안전하게 교환하기 위해 사용하는 인터페이스
Representational State Transfer(REST)
- API 작동 방식에 대한 조건을 부과하는 소프트웨어 아키텍처
- REST 아키텍처 스타일을 따르는 API를 REST API
- REST 아키텍처를 구현하는 웹 서비스를 RESTful 웹 서비스
서버 개발에서 가장 많이 하는 일
- 새로운 정보와 기존 정보를 가지고 정해진 로직을 수행하는 일
@Controller - 사용자와의 상호 작용 처리
@Service - Data Access를 선택할지 결정
@Repository - 데이터 소스와 연결
H2
- In-memory DB : 서버 작동하는 동안에만 내용 저장, 작동 멈추면 데이터 모두 삭제
- 연습용으로 사용
MYSQL
- 서비스 배포시 사용
- AWS RDS 서비스를 사용해 붙여볼 것
- 스프링과 궁합 개좋음
테이블이나 관계의 구조를 생성하는데 사용
데이터 사용권한 관리하는데 사용
테이블에 데이터를 검색, 삽입, 수정, 삭제하는데 사용
CREATE TABLE 테이블이름
(
필드이름 필드타입 AUTO_INCREMENT,
// id bigint AUTO_INCREMENT,
...
);
CREATE TABLE 테이블이름
(
필드이름 필드타입 NOT NULL,
...
);
CREATE TABLE 테이블이름
(
필드이름 필드타입 UNIQUE,
...
);
CREATE TABLE 테이블이름
(
필드이름 필드타입 PRIMARY KEY,
...
);
PRIMARY KEY (PK, 기본키)
테이블 내에서 유일하게 존재하는 값의 조합을 설정해서 중복된 데이터가 테이블에 삽입되는 것을 방지하는 제약조건
사용이유
- 데이터 중복 방지
- 기본키 설정하지 않는다면 최근 번호나 주소 등이 바뀌었다면 어느 정보가 정확한 정보인지 판단 어려움
- 즉, 데이터의 무결성이 깨짐
- 데이터를 매우 빠르게 찾을 수 있게 됨
- 기본키 설정하면 DBMS는 인덱스를 만듦
- 인덱스는 일종의 목차
- 예를 들어 주민등록번호 컬럼에 기본키 설정이 되어 있지 않다면
- 주민등록번호 중복될 수 있는 것을 가정하여 5000만 row를 전부 확인해야함
- 하지만 기본키가 설정되어 있다면 row를 전부 확인하지 않고 1개만 찾으면 바로 해당 데이터 반환
CREATE TABLE 테이블이름
(
필드이름 필드타입,
...
FOREIGN KEY(필드이름)
REFERENCES 테이블이름(필드이름)
);
FOREIGN KEY (FK, 외래키)
- 두개의 테이블을 연결하는 다리 역할
- 중복되는 데이터를 없애고 주문 테이블에서 외래키를 사용해서 사용자 테이블에 접근해 주문을 한 사용자의 정보도 가져올 수 있게됨
CREATE TABLE 테이블이름
(
필드이름 필드타입,
...
FOREIGN KEY(필드이름)
REFERENCES 테이블이름(필드이름) ON DELETE CASCADE
//ON UPDATE CASCADE
);
하지만 JOIN을 위해 외래키 설정하는 것이 무조건 좋은 것은 아님
외래키 설정하면 데이터 무결성 확인하는 추가 연산이 발생하고
또한, 무결성을 지켜야 하므로 상황에 따라 개발하는데 불편할 수 있음
SELECT * FROM 테이블명; 테이블 전체 불러오기 // SELECT * FROM 테이블명 WHERE 필드명 = ‘WHERE 다음 해당 필드값’; 테이블의 해당 필드값에 해당하는 전체 데이터만 불러오기 // SELECT 필드명1, 필드명2 FROM 테이블명 WHERE 필드명 = ‘WHERE 다음 해당 필드값’ 테이블 필드값에 해당하는 필드명1, 필드명2 의 값 불러오기 // SELECT 해당테이블별칭.필드명1, 해당테이블별칭.필드명2, 해당테이블별칭.필드명3 FROM 해당테이블명 별칭 JOIN 붙힐테이블명 별칭 ON 해당테이블별칭.(SELECT 뒤에 썼던 필드명 중 겹치는)필드명 = 붙힐테이블별칭.( SELECT 뒤에 썼던 필드명 중 겹치는)필드명; SELECT s.name, s.major_code, m.major_name FROM STUDENT s JOIN MAJOR m ON s.major_code = m.major_code; SELECT s.name, s.major_code, m.major_name FROM STUDENT s, MAJOR m WHERE s.major_code = m.major_code; 해당테이블이 앞으로 오게 해서 겹치는 필드명 기준으로 필드명1,2,3 붙게끔 합치기
PK : Primary key 기본키
FK : Foreign key 외래키
CONSTRAINT manager_fk_student_code foreign key (student_code) references student(student_code) //FK는 CONSTRAINT 이름을 ‘manager_fk_student_code’ 로 //student_code는 STUDENT 테이블을 참조하는 FK //references는 FK와 같이 쓰이며 참조할 테이블 앞에 씀
자바 ORM 기술에 대한 표준 명세
관계 | 코드 선언 | Entity | 예시 |
---|---|---|---|
일대다 (1:N) | @OneToMany | - Order (1) : Food (N) | 배달 주문 1개에 음식 여러개 선택 가능 |
다대일 (N:1) | @ManyToOne | Owner (N) : Restaurant(1) | 음식점 주인 여러명이 하나의 음식점을 소유 가능 |
일대일 (1:1) | @OneToOne | Order (1) : Coupon (1) | 배달 주문 1개 주문 시, 쿠폰 1개만 할인 적용 가능 |
다대다 (N:N) | @ManyToMany | User (N) : Restaurant(N) | 고객은 음식점 여러개 찜 가능 음식점은 고객 여러명에게 찜 가능 |
:: entity package
Member class
package com.sparta.springjpa.entity; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import java.util.ArrayList; import java.util.List; @Getter @Entity @NoArgsConstructor // 기본생성자 만들어주는 public class Member { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long Id; @Column(nullable = false) private String memberName; @OneToMany(mappedBy = "member", fetch = FetchType.EAGER) private List<Orders> oders = new ArrayList<>(); public Member(String memberName) { this.memberName = memberName; } }
Food class
package com.sparta.springjpa.entity; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import java.util.ArrayList; import java.util.List; @Getter @Entity @NoArgsConstructor public class Food { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String foodName; @Column(nullable = false) private int price; @OneToMany(mappedBy = "food", fetch = FetchType.EAGER) private List<Orders> oders = new ArrayList<>(); public Food(String foodName, int price) { this.foodName = foodName; this.price = price; } }
Orders class
package com.sparta.springjpa.entity; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; @Getter @Entity @NoArgsConstructor public class Orders { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne @JoinColumn(name = "food_id") private Food food; @ManyToOne @JoinColumn(name = "member_id") private Member member; public Orders(Food food, Member member) { this.food = food; this.member = member; } }
:: repository package
MemberRepository interface
package com.sparta.springjpa.repository; import com.sparta.springjpa.entity.Member; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; public interface MemberRepository extends JpaRepository<Member, Long> { Optional<Member> findByMemberName(String memberName); }
FoodRepository interface
package com.sparta.springjpa.repository; import com.sparta.springjpa.entity.Food; import org.springframework.data.jpa.repository.JpaRepository; public interface FoodRepository extends JpaRepository<Food, Long> { }
OrdersRepository interface
package com.sparta.springjpa.repository; import com.sparta.springjpa.entity.Orders; import org.springframework.data.jpa.repository.JpaRepository; public interface OrdersRepository extends JpaRepository<Orders, Long> { }
:: com.sparta.springjpa package
java folder 바로 아래
package com.sparta.springjpa;
import com.sparta.springjpa.entity.Food; import com.sparta.springjpa.entity.Member; import com.sparta.springjpa.entity.Orders; import com.sparta.springjpa.repository.FoodRepository; import com.sparta.springjpa.repository.MemberRepository; import com.sparta.springjpa.repository.OrdersRepository; import lombok.RequiredArgsConstructor; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; @Component @RequiredArgsConstructor public class Restaurant implements ApplicationRunner { private final FoodRepository foodRepository; private final OrdersRepository ordersRepository; private final MemberRepository memberRepository; @Override public void run(ApplicationArguments args) throws Exception { List<Food> foods = new ArrayList<>(); Food food1 = new Food("후라이드", 10000); foods.add(food1); Food food2 = new Food("양념치킨", 12000); foods.add(food2); Food food3 = new Food("반반치킨", 13000); foods.add(food3); Food food4 = new Food("고구마피자", 9000); foods.add(food4); Food food5 = new Food("아보카도피자", 110000); foods.add(food5); foodRepository.saveAll(foods); List<Member> members = new ArrayList<>(); Member member1 = new Member("삼식이"); members.add(member1); Member member2 = new Member("먹깨비"); members.add(member2); memberRepository.saveAll(members); System.out.println("=================================================================="); System.out.println("Member 데이터"); List<Member> findMembers = memberRepository.findAll(); for (Member findMember : findMembers) { System.out.println("findMember = " + findMember.getMemberName()); } System.out.println("=================================================================="); System.out.println("Food 데이터"); List<Food> findFoods = foodRepository.findAll(); for (Food findFood : findFoods) { System.out.println("findFood = " + findFood.getFoodName()); } List<Orders> ordersList = new ArrayList<>(); Orders orders1 = new Orders(findFoods.get(0), findMembers.get(0)); ordersList.add(orders1); Orders orders2 = new Orders(findFoods.get(3), findMembers.get(1)); ordersList.add(orders2); Orders orders3 = new Orders(findFoods.get(4), findMembers.get(1)); ordersList.add(orders3); Orders orders4 = new Orders(findFoods.get(2), findMembers.get(0)); ordersList.add(orders4); Orders orders5 = new Orders(findFoods.get(2), findMembers.get(0)); ordersList.add(orders5); Orders orders6 = new Orders(findFoods.get(1), findMembers.get(1)); ordersList.add(orders6); Orders orders7 = new Orders(findFoods.get(1), findMembers.get(0)); ordersList.add(orders7); Orders orders8 = new Orders(findFoods.get(3), findMembers.get(1)); ordersList.add(orders8); ordersRepository.saveAll(ordersList); System.out.println("=================================================================="); int num = 1; System.out.println("Orders 데이터"); List<Orders> orderList = ordersRepository.findAll(); for (Orders orders : orderList) { System.out.println(num); System.out.println("주문한 사람 = " + orders.getMember().getMemberName()); System.out.println("주문한 음식 = " + orders.getFood().getFoodName()); num++; } System.out.println("=================================================================="); System.out.println("삼식이 주문한 음식"); Member samsik = memberRepository.findById(1L).orElseThrow( ()->new RuntimeException("없음") ); num = 1; for (Orders orders : samsik.getOders()) { System.out.println(num); System.out.println("주문한 음식 = " + orders.getFood().getFoodName()); System.out.println("주문한 음식 가격 = " + orders.getFood().getPrice()); num++; } System.out.println("=================================================================="); System.out.println("아보카도피자 주문한 사람"); Food abocado = foodRepository.findById(5L).orElseThrow( ()->new RuntimeException("없음") ); for (Orders order : abocado.getOders()) { System.out.println("주문한 사람 = " + order.getMember().getMemberName()); } Member member = memberRepository.findByMemberName("삼식이").orElseThrow( () -> new RuntimeException("삼식이 없음") ); System.out.println("member.getMemberName = " + member.getMemberName()); System.out.println("member.getMemberName = " + member.getId()); } }
Spring Data JPA 의 Query Methods
예제
상품 Entity 선언
@Entity public class Product extends Timestamped { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private Long userId; private String title; private String image; private String link; private int lprice; private int myprice; }
상품 Repository 생성
public interface ProductRepository extends JpaRepository<Product, Long> { }
기본 제공해 주는 기능
// 1. 상품 생성 Product product = new Product(...); productRepository.save(product); // 2. 상품 전체 조회 List<Product> products = productRepository.findAll(); // 3. 상품 전체 개수 조회 long count = productRepository.count(); // 4. 상품 삭제 productRepository.delete(product);
ID 외의 필드에 대한 추가 기능은 interface 만 선언, 구현은 Spring Data JPA 가 대신
public interface ProductRepository extends JpaRepository<Product, Long> { // (1) 회원 ID 로 등록된 상품들 조회 List<Product> findAllByUserId(Long userId); // (2) 상품명이 title 인 관심상품 1개 조회 Product findByTitle(String title); // (3) 상품명에 word 가 포함된 모든 상품들 조회 List<Product> findAllByTitleContaining(String word); // (4) 최저가가 fromPrice ~ toPrice 인 모든 상품들을 조회 List<Product> findAllByLpriceBetween(int fromPrice, int toPrice); }