|  정규화와 비정규화에 대한 개념이 정리된 포스팅이 있어 가져와 보았다.

https://owlyr.tistory.com/20

 

[데이터베이스] 정규화 vs. 비정규화(반정규화)

💡 정규화 vs. 비정규화(반정규화) 데이터베이스 정규화 데이터베이스 비정규화 데이터베이스 비정규화(Denormalization, 반정규화)란 무엇인가? 장점, 단점 비정규화 대상 주의점 정규화(Normalized)란

owlyr.tistory.com

 

|  간단 요약

정규화란, RDBMS에서의 데이터의 중복을 최소화하는 작업을 말한다. 

> 정규화를 많이 하게 되면 조인을 많이 해야하는 단점이 있다.

비정규화란, 반면 의도적으로 데이터를 중복시키거나, 데이터를 그룹핑함으로써, 성능을 최적화하는 작업이다.

  SQL 문법 실제 처리 순서  
1 SELECT  count(*) as c FROM  member 테이블에서
직업이 개발자인 사람들을 추출
2 FROM  member SELECT 
3 WHERE  job = '개발자' WHERE
4 GROUP BY nationality GROUP BY 국적에 따라 그룹핑
5 HAVING avg(salary) >= 3000 HAVING 평균 연봉이 3000 이상인 경우로 한정
6 ORDER BY c ORDER BY 순서 지정

 

** WHERE와 HAVING의 차이점

  WHERE HAVING
위치 FROM 뒤에 GROUP BY 뒤에
언제 SELECT한 레코드의 조건 정의 그룹화되어 만들어진 레코드 집합의 조건 정의

 

 

[ 참조 및 출처 ]

부트캠프 수업 참조

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=likemony&logNo=221183607855 

https://wikidocs.net/3943

|  Enum이란?

- Enumeration Type : 열거체를 말한다.

- 핵심 : 사용자 지정 타입이며 실제 값은 0,1,2,3... 순번으로 출력된다.

- 부가 : 타입에 괄호()를 넣어 그 타입에 대해 설명할 수 있다.

 

|  Enum 문법

- 기본적으로 아래와 같이 열거하여 사용하는데

public enum AnimalType {
    CAT, DOG, HAMSTER, CHICKEN, SHEEP, LAMAR;
}

- 상수타입 옆에 괄호()를 넣어 상숫값을 명시할 수 있다.

public enum AnimalType {

    CAT("포유류","고양이"),
    DOG("포유류","개"),
    HAMSTER("포유류","햄스터"),
    CHICKEN("조류","닭"),
    SHEEP("포유류","양"),
    LAMAR("포유류","라마");

    private String species;
    private String kind;

    AnimalType(String species, String kind) {
        this.species = species;
        this.kind = kind;
    }

    public String getSpecies() {
        return species;
    }

    public String getKind() {
        return kind;
    }
}

- Main에서 테스트한 결과

import type.AnimalType;

import java.util.Optional;

public class Main2 {

    static class Patient {

        private Long id;
        private String patientNo;
        private AnimalType animalType;
        private String name;

        public Patient(Long id, String patientNo,
                       AnimalType animalType, String name) {
            this.id = id;
            this.patientNo = patientNo;
            this.animalType = animalType;
            this.name = name;
        }

        @Override
        public String toString() {
            return id + ".환자번호(" + patientNo + ") : "
                    + name + " (" + animalType.getSpecies()
                    + " " + animalType.getKind() + ")";
        }
    }

    public static void main(String[] args) {
        Patient patient = new Patient(1L, "1000101",
                AnimalType.CAT, "봄");

        Optional<Patient> patient2 = Optional.ofNullable(patient);
        System.out.println(patient2); // Optional[1.환자번호(1000101) : 봄 (포유류 고양이)]
    }
}

 

[ 참조 및 출처 ]

http://www.tcpschool.com/java/java_api_enum

'Language > Java' 카테고리의 다른 글

[이펙티브 자바] 객체의 파괴  (0) 2022.10.03
[이펙티브 자바] 객체의 생성  (0) 2022.09.29
JAVA 라이브러리 - Optional<T> 클래스  (0) 2022.09.15
SOLID 원칙  (0) 2022.08.29
JAVA 라이브러리 - 컬렉션  (0) 2022.08.11

|  Optional<T> 클래스란?

- 자바에서 모든 객체는 Referece Type으로 nullable하다. * null이 발생할 수 있는 타입

  > Referece Type의 객체는 null을 발생시킬 수 있다. -- NullPointerException

- Optional<T>은 객체를 nullable하게 쓸 수 있도록 하는 Wrapper class로, 객체 사용 시 null을 명시적으로 처리하게 한다.

  * 참고로 코틀린의 경우, nullable한 타입을 구분하는 코드가 별도로 존재한다.

Optional은 주로 "결과 없음"을 명확하게 나타내야 하고, 
null을 사용하면 오류가 발생할 수 있는 메소드 반환 유형으로 사용된다. 
Optional 변수는 null일 수 없으며 항상 Optional 인스턴스를 가리켜야한다. -- Oracle Docs

- 장점 : null을 직접 핸들링하지 않음, null 여부를 타입만으로 나타낼 수 있음, 체이닝을 통한 중간 및 종단 처리

  Optional<T> Optional.empty()
  T null
더보기

- Optional의 생성자를 보면, 아래와 같이 Objects.requireNonNull()을 통해

  감싸려는 객체 데이터가 null일 때, NullPointException을 발생시키도록 한다.

- 따라서 Optional로 감싼다고 하여도 NullPointException이 발생하지 않는 것은 아니다.

private Optional() {
    this.value = null;
}

private Optional(T value) {
    this.value = Objects.requireNonNull(value); // null이면 NullPointException 발생
}
public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

 

 

 

|  Optional 객체의 맴버 변수와 생성자

private static final Optional<?> EMPTY = new Optional<>();

private final T value;

private Optional() {
    this.value = null;
}

 

|  Optional 객체의 메소드

isPresent(): boolean null인지 아닌지 여부를 boolean으로 반환
of() 데이터를 Optional<T>로 감싼다.
ofNullable() of()와 동일하나 null이 발생할 때 처리 **null 발생 시 꼭 이 메소드 사용
get() null이 아닌 경우, 해당값 반환 **null일 때 예외 발생시키므로, 사용 시 주의요함
orElse(<T>) null일 경우, 특정 데이터를 반환
orElseGet(() -> getMethod()) null일 경우, 함수형 메소드 실행
orElseThrow(()->new Exception()) null일 경우, 지정한 예외 발생
filter() 조건식에 따라 필터링
map() 조건식에 따라 데이터를 매핑
flatMap() map과 동일하나 null처리가 다름

 

(1) of()와 ofNullable()의 차이

- ofNullable()의 경우 null이 발생할 때 empty() 메소드를 사용하는데, empty() 메소드를 쓰면 Empty 맴버 변수를 반환한다.

private static final Optional<?> EMPTY = new Optional<>();

public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}

(2) isPresent(), ifPresent() 

public boolean isPresent() {
    return value != null;
}

public void ifPresent(Consumer<? super T> consumer) {
    if (value != null)
        consumer.accept(value);
}

(3) get(), orElse(), orElseGet(), orElseThrow()

- get()은 사용을 지양하는 편이 좋다고 한다.* 감싼 데이터값이 null일 때, NoSuchElementException이 발생

public T get() {
    if (value == null) {
        throw new NoSuchElementException("No value present");
    }
    return value;
}

public T orElse(T other) {
    return value != null ? value : other;
}


public T orElseGet(Supplier<? extends T> other) {
    return value != null ? value : other.get();
}

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if (value != null) {
        return value;
    } else {
        throw exceptionSupplier.get();
    }
}

- orElse()과 orElseGet()의 차이점은 인자로 함수(ex. getRandomNumber())를 전달할 때 

  > orElse()의 경우 null이 발생하지 않아도 함수를 실행한 후 결과값을 orElse(//결과값//)에 전달하는 한 편,

  > orElseGet()을 쓰면 Supplier라고 하는 함수형 인터페이스를 통해

                                    null이 발생할 때에만 함수를 실행한뒤 결과값을 전달한다.

// 가정: optionalUser.findById(userId)는 null X

// getRandomUser() 실행 --> 결과값이 orElse()안에 담김
optionalUser.findById(userId).orElse(getRandomUser());

// getRandomUser() 실행 X
optionalUser.findById(userId).orElseGet(() -> getRandomUser());

- orElseThrow()는 null 발생 시, 지정한 Exception을 반환하게 하는 메소드이다.

(4) filter(), map(), flatMap()

public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if (!isPresent())
        return this;
    else
        return predicate.test(value) ? this : empty();
}

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value));
    }
}

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Objects.requireNonNull(mapper.apply(value));
    }
}

 

|  언제쓸까? - 값이 없을 수도 있을 때

(1) DB를 조회할 때

// JPA을 통해 Optional<T> 타입의 반환값이 null일 경우 처리하는 법
Optional<User> user = userRepository.findById(userId)
                                    .orElseThrow(() -> new Exception());

String userName = userRepository.findUserNameById(userId)
				.map(user -> user.getUserName())
				.orElse("이름 없음");

(2) 조회한 객체에 특정 칼럼이 비어있을 때

@Entity(name = "user")
public class User{
    @Id
    @GeneratedValue
    private Long id;
    
    private String userId;
    private String password;
    
    private String thumbnail;
    
    public Optional<String> getThumbnail(){
    	return Optional.ofNuallable(thumbnail);
    }
    
}

(3) 기존 시스템의 리턴 값이 null을 리턴할 때

Optional.ofNuallable(userRepository.findByName(userName))
		.ifPresent(user -> list.add(user));

(4) 필드에 Optional은 지양하기

- Optional은 함수 반환을 목적으로 만들어졌다.

- Serializable을 구현하지 않아 직렬화를 하면 이상한 값이 나온다.

[직렬화란]

메모리는 가상 메모리와 물리 메모리로 나뉘어져 있다.
가상 주소는 논리적인 주소일 뿐이지, 실제 저장되는 위치는 다를 수 있다.
객체를 그대로 저장하게 되면, 가상 주소까지 저장되게 되는데, 메모리 주소는 다른 시스템에 전달해도 의미가 없다.
따라서 불필요한 정보는 제외하고 타입 정보, 값 정보를 Byte형태로 만들어 보내는데 이를 직렬화라 한다.
직렬화를 하려면 클래스가 serializable를 구현해야한다.

- Optional.empty()를 빠뜨릴 확률이 높다. (휴먼 에러)

 

|  Optional을 제대로 쓰기

(1) 비어있을 컬렉션을 표현할 땐 Collections.emptyList()를 사용한다.

Optional<List<User>> findAll();  // !!사용하지 말아야 한다.

Collections.emptyList(); // Collections의 emptyList()를 사용한다.

- 컬렉션 요소에 Optional은 절대 금지

// 사용자가 Optional을 매번 판단해야하는 상황발생
List<Optional<User>> findAll();  // !!사용하지 말아야 한다. 

// ----사용시
for (Optional<User> opt : list) {

    // 매번 optional을 객체로 변환
    User user = opt.get(); // 불필요하다.

}

// User타입 그대로 반환
List<User> findAll();

(2) Optional을 파라미터로 넘기지 않는다.

(3) Optional 값을 가져올 때 .get()은 되도록 지양한다.

- get()을 쓰면, null일 때, NoSuchElementException을 발생시킨다.

- optional의 중단, 종단 메소드를 체이닝하여 사용하는게 낫다.

(4) Optional의 중간, 종단 메소드를 쓰기 위해 불필요한 옵셔널을 하지 않는다.

 

 

[ 참고 및 출처 ]

부트 캠프 수업을 들은 후 정리

https://tecoble.techcourse.co.kr/post/2021-06-20-optional-vs-null/

http://www.tcpschool.com/java/java_stream_optional

orElse()와 orElseGet()의 차이 : https://kdhyo98.tistory.com/40

'Language > Java' 카테고리의 다른 글

[이펙티브 자바] 객체의 생성  (0) 2022.09.29
JAVA 유용한 타입 - Enum  (0) 2022.09.15
SOLID 원칙  (0) 2022.08.29
JAVA 라이브러리 - 컬렉션  (0) 2022.08.11
JAVA 라이브러리 - 제네릭 클래스  (0) 2022.08.10

|  OOP의 핵심

[ 지난 번 OOP 관련 포스팅 내용 요약 ]
- OOP는 객체 간 상호 관계에 포커스를 맞춘 방법론으로, 추상화, 상속, 다형성, 캡슐화 4가지의 주요 특징이 있다.
- 추상화는 모델링이고, 상속은 확장과 재사용성이며, 다형성은 사용 편의, 캡슐화는 정보 은닉에 해당된다.
- 추상화 단계에서는 클래스 내 응집도를 높이는 게 필요하고, 상속 시에는 클래스 간 결합도에 주의해야 한다.

- OOP는 풀면 참 복잡했는데, 오늘 배운 수업에서는 분류교체라는 단어로 그 핵심을 단순화해 설명했다.

* 현업에 오래 종사했던 강사분은 스파게티 소스에 대해 언급하며,

  소프트웨어를 유연하게 만들려면 객체지향 방식이 유용하다 얘기주셨다.

분류한다 코드를 적절히 잘 분류한다. -> 클래스
교체한다 필요에 따라서 특정 모듈을 통째로 교체하기도 한다. -> DBMS 업체나 라이브러리를 통째로 교체하기도 한다.

※ 라이브러리와 프레임워크와 아키텍처 간의 상관관계 바로가기

 

|  SOLID 원칙

- 로버트 C. 마틴에 의해 만들어진 원칙

SRP 단일 책임 원칙(분류)    Simple Responsibility principle
OCP 개방 폐쇄 원칙(교체) Open/Closed principle
LSP 리스코프 치환 법칙(교체) Liskov subsituation principle
ISP 인터페이스 분리 원칙(분류) Interface segregation principle
DIP 의존성 역전 원칙(교체) Dependency inversion principle

 

※ 참조 : 실제 현장에서 사용하는 코드는 아래와 달라 보입니다. 원리를 이해하기 위한 코드로 봐주시면 감사하겠습니다.

 

1. SRP : 한 클래스는 단일의 책임만 가져야 한다.

 

SRP가 지켜지지 않은 코드 

class PaymentService{
    public void pay(Customer customer, Product product) {

        if (!isValidatedPrice(customer, product)) {
            throw new IllegalArgumentException("charge your money");
        }

        customer.setMoney(customer.getMoney() - product.getPrice());
    }

    public boolean isValidatedPrice(Customer customer, Product product) {
        if (customer.getMoney() < product.getPrice()) {
            return false;
        }

        return true;
    }

    public void sendSMS(Customer customer, Product product) {
        System.out.println("phone number : " + customer.getPhone());
        System.out.println("상품 " + product.getName() + "(" 
                          + product.getProduct_no() + ")이 결제되었습니다.");
    }
}

- 고객이 상품을 주문할 때, PaymentService라는 클래스에서 아래와 같이 세가지의 기능을 제공한다고 할 때에

  (1) 결제하기 - pay()

  (2) 결제가능 여부 확인하기 - isValidatedPrice()

  (3) 결제완료 SMS(메세지) 전송하기 - sendTMS()

- 이 세가지 기능은 엄밀히 말하면 서로 다른 기능이라 볼 수 있다. 

- 전체 scope이 작은 경우에는 세가지 기능이 묶일 수도 있겠지만, 만약 기능이 더 복잡해지고 확장된다면 세가지를 분리할 수 있다.

 

클래스를 분리

class PaymentService{
    public void pay(Customer customer, Product product) {

        if (!PaymentValidation.isValidatedPrice(customer, product)) {
            throw new IllegalArgumentException("charge your money");
        }

        customer.setMoney(customer.getMoney() - product.getPrice());
    }
}
class PaymentValidation{
    public static boolean isValidatedPrice(Customer customer, Product product) {
        if (customer.getMoney() < product.getPrice()) {
            return false;
        }

        return true;
    }
}
class SMSService{
    public void sendSMS(Customer customer, Product product) {
        System.out.println("phone number : " + customer.getPhone());
        System.out.println("상품 " + product.getName() + "(" 
                           + product.getProduct_no() + ")이 결제되었습니다.");
    }
}

 

2. OCP : 확장에는 열려있고, 변경에는 닫혀있다.

* if-else의 반복적인 케이스가 보일 때에는 클래스를 분리하는 것이 효과적일 수 있다.

 

OCP가 지켜지지 않은 코드

class PointService {
    public double getPointRate(String category) {
        // 구매 카테고리별로 포인트 비율을 다르게 지급
        if (category.equals("옷")){
            return 0.05;
        } else if (category.equals("잡화")) {
            return 0.02;
        } else if (category.equals("기타")) {
            return 0.01;
        }

        return 0;
    }

    public double getVIPRate(String category){
        // 구매 카테고리 별로 VIP 여부에 따라서 비율을 다르게 지급
        if (category.equals("잡화")){
            return 0.12;
        } else if (category.equals("도서")) {
            return 0.08;
        } else if (category.equals("기타")) {
            return 0.01;
        }

        return 0;
    }
}

- 만약에 포인트 제도가 추가되어, 구매 카테고리 별로 서로 다르게 포인트를 지정한다고 하자.

- 이럴때 카테고리가 위와 같이 한정적이라면 상관이 없겠지만 카테고리가 계속 늘어난다면 문제가 발생할 수 있다.

- 더불어 getPoint()와 getVIPRate()의 카테고리명과 순서가 다른 걸 볼 수 있다.

- 이런 경우 로직이 통일적이지 않아지기 때문에 결과적으로 문제가 발생할 수 있다.

 

인터페이스를 통한 OCP 확보

interface PointServiceIF{
    public abstract double getPointRate(String category);
    public abstract double getVIPRate(String category);
}

class Clothe implements PointServiceIF{

    @Override
    public double getPointRate(String category) {
        return 0.05;
    }

    @Override
    public double getVIPRate(String category) {
        return 0.00;
    }
}

class Book implements PointServiceIF{

    @Override
    public double getPointRate(String category) {
        return 0.12;
    }

    @Override
    public double getVIPRate(String category) {
        return 0.25;
    }
}

- 인터페이스를 통해서 메소드를 프로토타입으로 만들어 준 후에, 각각의 카테고리를 클래스로 만들어 메소드를 구현하면 OCP를 확보할 수 있다.

 

3. LSP : 서브타입은 언제나 기반타입으로 교체할 수 있어야 한다.

- 이 부분은 상위 클래스(또는 인터페이스)의 기능과 동일한 기능을 하위 클래스가 상속(또는 구현)하는 가를 말한다.

상속 is kind of  하위 클래스는 상위 클래스의 한 분류이다.
구현 is able to 하위 클래스는 상위 인터페이스를 구현할 수 있다.

- 여기서는 예제를 만들지 않고 대신 예제 링크만 가져왔다.

https://blog.itcode.dev/posts/2021/08/15/liskov-subsitution-principle

- 위 예제에서는, 직사각형 > 정사각형으로 LSP를 설명하는데, 정사각형은 직사각형의 메소드를 모두 동일하게 가져가지 않기 때문에, 사각형 클래스를 만들고 직사각형과 정사각형이 그것을 상속받게 했다.

더보기

- 실무에서는 상속을 많이 사용하지 않는다고 한다.

왜냐하면, 오버라이딩을 했는지 안 했는지 매번 확인하기가 어렵고,

오버라이딩 잘못하면 로직 충돌 (Fragile base class문제)이 일어나기도 하며, 

기능을 너무 확장하거나 변경하면 재활용성이 낮아지기 때문이라고 한다.

 

- 그래서 상속의 대안 및 상속을 잘하기 위해서는

(1) 상위 클래스를 한 클래스만 상속하거나

(2) 클래스 상속이 아닌, 인터페이스로 대체하거나

(3) 상위 클래스와 상호 치환(=동일 기능을 갖도록)이 가능하도록 상속을 해야한다. 

 

4. ISP : 인터페이스도 단일 책임을 갖도록 분리해야 한다.

* 인터페이스를 너무 크게 만들면, 사용하지 않는 메소드들도 구현하도록 해야 한다.

* 실무에서 보면 단일 메소드를 가진 인터페이스들이 있다. > 세분화된 인터페이스로 쪼개서 단일 책임을 갖게 한 것

 

- 이 부분은 별도로 내용을 추가하지 않고, 바로 앞의 예시로 이야기하겠다.

interface PointServiceIF{
    public abstract double getPointRate(String category);
    public abstract double getVIPRate(String category);
}

class Clothe implements PointServiceIF{

    @Override
    public double getPointRate(String category) {
        return 0.05;
    }

    @Override
    public double getVIPRate(String category) {
        return 0.00;
    }
}

class Book implements PointServiceIF{

    @Override
    public double getPointRate(String category) {
        return 0.12;
    }

    @Override
    public double getVIPRate(String category) {
        return 0.25;
    }
}

- 바로 위의 코드를 보면 "옷"의 경우 안 쓰는 메소드인 getVIPRate()를 구태어 어거지로 구현한 걸 볼 수 있는데,

  이유는 인터페이스 안에 메소드를 두 개를 넣어 두어서 두 가지를 모두 구현해야 하기 때문이다.

- 이렇듯 상속은 주의해서 사용하는 게 좋다. 필요하지 않은 메소드까지 구현해야할 수 있고,

  너무 많이 확장되고 재사용되다보면 최초에 목적하고 있던 기능에서 많이 동떨어진 메소드가 만들어질 수 있기 때문이다.

- 이러한 문제를 해결하기 위해서는 단일 인터페이스, 곧 단일 메소드를 가진 인터페이스를 만드는 것이 좋다.

interface GeneralPointServiceIF{
	public abstract double getPointRate(String category);
}

interface VIPPointServiceIF{
	public abstract double getVIPRate(String category);
}

class Clothe implements GeneralPointServiceIF{
    @Override
    public double getPointRate(String category) {
        return 0.05;
    }
}

class Book implements GeneralPointServiceIF, VIPPointServiceIF{
    @Override
    public double getPointRate(String category) {
        return 0.12;
    }

    @Override
    public double getVIPRate(String category) {
        return 0.25;
    }
}

 

5. DIP : 하위 모듈의 변경이 상위 모듈의 변경을 요구하는 의존성을 끊어내야 한다.

- 개발을 하다보면 보안상의 이슈가 발생한 라이브러리를 다른 라이브러리로 교체해야할 때가 있다고 한다.

- 또는 외주업체에 맡겨 서비스를 추가하는 경우도 있는데, 업체를 변경할 때에도 기존 서비스를 변경해야할 때가 있다고 한다.

- 만약 호텔에 Deposit 금액으로 20만원을 넣어두고, 호텔의 서비스를 사용할 때마다 deposit 금액을 차감시킨다고 하자.

[1] 호텔 비용 정산 클래스 - HotelPaymentService

[2] 고객 deposit에 연결하는 클래스 - HotelDepositAdapter

class HotelPaymentService{
    String pay(Customer customer, ServiceRequest serviceRequest){
    	HotelDepositAdapter hotelDepositAdapter = new HotelDepositAdapter();
        int payResult = hotelDepositAdapter.useService(customer, serviceRequest);
    	
        if (payResult == -1){
        	return "Fail";
        }
        
        return "Success : left deposit - " + payResult + "(원)"; 
    }
}

class HotelDepositAdapter{  
    int useService(Customer customer, ServiceRequest serviceRequest){
    	int possibleDeposit = customer.getDeposit() - serviceRequest.getServicePrice();
        
        if (possibleDeposit < 0) {
        	return -1;
        }
        
        return possibleDeposit;
    }
}

- 이 상태에서, 만약 호텔측에서 Deposit 금액 외에 카드를 맡기면 추가적으로 결제가 가능하게 해달라는 요청을 한다면

[1] 호텔 비용 정산 클래스 - HotelPaymentService

[2] 고객 deposit에 연결하는 클래스 - HotelDepositAdapter

[3] 고객 card에 연결하는 클래스 - HotelCardAdapter

- 위와 같이 card를 연결하는 어뎁터 클래스를 하나 더 추가하게 될 수 있다.

- 이런 경우 HotelPaymentService이 이미 HotelDepositAdapter에 의존적이라 코드를 전반적으로 수정해주어야 한다.

- 앞으로 비용 정산 방식이 추가적으로 바뀌게 되면 이 방식은 비효율적이다

class HotelPaymentService{
    String pay(Customer customer, ServiceRequest serviceRequest, PaymentType type){
    	
        if (type.getType() < 0 || type.getType() >= 2) {
        	return "PaymentRequest Error : write correct PaymentType";
        }
        
        int payResult = -1;
        
        if (type.getType() == 0){
        	HotelDepositAdapter hotelDepositAdapter = new HotelDepositAdapter();
        	payResult = hotelDepositAdapter.useService(customer, serviceRequest);
        } else if (type.getType() == 1){
        	HotelCardAdapter hotelCardAdapter = new HotelCardAdapter();
        	payResult = hotelCardAdapter.useService(customer, serviceRequest);
        } 
    	
        if (payResult == -1){
        	return "Fail";
        }
        
        return "Success : left deposit - " + payResult + "(원)"; 
    }
}

- 이런 경우 아래와 같이 어뎁터 자체가 인터페이스를 구현하도록 하고,

  HotelPaymentService가 인터페이스를 바라보도록 하면 해결된다.

enum HotelPaymentType{
    DEPOSIT, CARD
}

class HotelPaymentService{

    private final HotelDepositAdapter hotelDepositAdapter = new HotelDepositAdapter();
    private final HotelCardAdapter hotelCardAdapter = new HotelCardAdapter();

    String pay(Customer customer, ServiceRequest serviceRequest){
    
    	HotelPaymentIF hotelPaymentIF;
        
        if (serviceRequest.getPaymentType() == HotelPaymentType.DEPOSIT){
        	hotelPaymentIF = hotelDepositAdapter;
        } else {
        	hotelPaymentIF = hotelCardAdapter;
        }
    
    	int payResult = hotelPaymentIF.useService(customer, serviceRequest);
    	
        if (payResult == -1){
        	return "Fail";
        }
        
        return "Success : left deposit - " + payResult + "(원)"; 
    }
}

interface HotelPaymentIF{
	int useService(Customer customer, ServiceRequest serviceRequest);
}

class HotelDepositAdapter implements HotelPaymentIF{  
    int useService(Customer customer, ServiceRequest serviceRequest){
    	int possibleDeposit = customer.getDeposit() - serviceRequest.getServicePrice();
        
        if (possibleDeposit < 0) {
        	return -1;
        }
        
        return possibleDeposit;
    }
}

class HotelCardAdapter implements HotelPaymentIF{  
    int useService(Customer customer, ServiceRequest serviceRequest){
    	if (CardService.useCard(customer, serviceRequest) < 0) {
        	return -1;
        }
        
        return serviceRequest.getServicePrice();
    }
}

 

 

[ 출처 및 참조 ]

부트캠프 강의를 들은 후 정리한 내용입니다.

[스프링 입문을 위한 자바 객체지향의 원리와 이해]

SOLID https://bottom-to-top.tistory.com/27

위키백과 https://ko.wikipedia.org/wiki/SOLID_(%EA%B0%9D%EC%B2%B4_%EC%A7%80%ED%96%A5_%EC%84%A4%EA%B3%84) 

 

|  Exerd를 사용해 설계해보기

- 부트캠프 실습 후 간단하게 배운 내용을 복습하고자 사용법만 간략히 정리하려고 한다.

* 참고로 Exerd를 설치하는 방법은 이전 포스팅에서 다루었다.

1. 도구 설명

no 설명 비고
0 Logical / Physical 토글 버튼  
1 테이블 생성 칼럼 추가 : [Alt] + [Enter]
2 - 점선 : 비식별 관계  >> pk를 추가하여 연결
- 실선 : 식별 관계      >> fk로 추가하여 연결
 
3 마우스 우측 클릭 후, [논리/물리 같이 보기] 버튼 터치하면 함께 보기 가능  
4 도메인 및 데이터 타입 등에 대한 설명 보기  

 

2. 데이터 타입 및 제약 조건 지정하기

(1) 하단의 데이터 타입 끌어오기

- 하단에 보면 샘플로 만들어진 데이터타입이 이미 있는데 이걸 끌어오는 방법

 

(2) 하나씩 직접 입력하는 법

- 테이블의 칼럼을 더블클릭하면 데이터 타입을 직접 입력할 수 있다.

 

(3) 테이블 전체 칼럼 입력하는 법

- 테이블을 선택하고, 우측마우스 - [특정(속성)] - [칼럼] 을 누르면 아래와 같이 창이 나온다.

- 여기에서 전체적으로 작성이 가능하며, [일반] 탭의 "자동 증가" 박스를 체크하면 auto-increment가 된다.

 

3. DDL로 변환하기

- 좌측 또는 우측에 나와있을 [모델]에서 Schema명을 수정하고, 테이블도 논리/물리 모두 잘 작성했다면

- [Help] - [eXERD] - [포워드 엔지니어링] 을 통해 DDL로 변환한다.

* "이름 앞에 스키마 표시"를 하게 되면 명령어를 제대로 인식을 못하므로 비체크 후 [Next]

- 변환된 DDL 결과물을 클립보드로 복사해서 DataGrip이나 DBeaver와 같은 DB툴에서 사용할 수 있다.

 

[ 참고 및 출처 ]

부트캠프 수업을 들은 후 정리한 내용입니다.

|  모델링이란?

- 모델링이란? 비즈니스 목적에 맞게 현실세계의 데이터를 도식화하는 것 

- 왜 모델링을 하는가? 비즈니스 목적에 부합하면서도, 효율적인 자원 관리를 하기 위해

- 모델링의 특징

추상화 현실 세계의 실재를 도식화 하는 과정
단순화 현실 세계의 현상을 약속된 규약에 의해 제한된 표기법 및 언어로 쉽게 표현하는 과정
명확화 모두가 이해할 수 있도록 모호함을 제거하고 정확하게 현상을 기술하는 과정

- 모델링을 바라보는 다른 시각

모델링 데이터  업무의 내용 (무엇을 - Data).

- 요구사항 X는 어떤 데이터와 연관되는가?
프로세스  업무의 처리 (어떻게 - Process).

- 요구사항 X와 연관된 기존 시스템은 뭔가?
- 요구사항 X를 위한 전반적인 프로세스는 뭔가? 
상관 관계 데이터와 프로세스간의 관계

- 업무 처리 방법(프로세스)에 따라 데이터는 어떻게 영향을 받는가?

 

|  데이터 모델링

- 데이터의 모델링이란? 정보시스템을 구축하기 위한 데이터 기반 업무 분석 기법

- 데이터 모델링 3단계

출처:https://blog.naver.com/PostView.nhn?blogId=qbxlvnf11&logNo=221225469375

단계 개념적 데이터 모델링(Conceptual) 논리적 데이터 모델링(Logical) 물리적 데이터 모델링(Physical)
정의 현실세계의 실재를 높은 추상화
수준으로 형성화하는 과정
비즈니스 정보의 논리적인 구조와
규칙을 명확하게 표현하는 과정
실제 DBMS에 데이터를 어떻게
저장할지 정의하는 과정
하는일 엔터티 및 엔터티간 관계 파악 트랜잭션 인터페이스 설계,
정규화, 참조 무결성 규칙 정의,
M:M관계 해소 등
트랜잭션 세부사항을 설계,
물리적 저장 구조(db,table..),
자료 추출 접근 방법 등을 정함

* 트랜잭션 : 쪼갤 수 없는 업무의 최소 단위 (ex. 물건을 산다)

* 일반적으로 개념적 모델링과 논리적 모델링을 함께 진행하는 경우가 많다고 한다.

 

|  데이터 모델링 표기법

- 다양한 표기법이 있지만 크게 아래와 같이 두 가지 방법에 대해서 많이 사용된다고 한다. 

피터첸 표기법 (E-R Entity-relationship Model) - Entity : 사각형
- Relationship : 마름모
- Attribute : 타원형
IE/Crow's Foot 표기법 Erwin에서 보는 발톱모양의 관계선에 해당

더보기

[ ER WIN 프로그램에서 쓰는 표기법에 대한 상세 설명 ]

* 출처 : https://lipcoder.tistory.com/330

- 그 중 피터첸 곧, E-R모델이 가장 많이 사용된다고 하는데, 자세한 내용은 아래와 같다.

피터첸_예시__출처:https://lipcoder.tistory.com/330

Entity 객체, 실체 사람, 장소, 물건, 사건, 개념 등의 명사

1) 업무와 연관되며 관리가 필요한 정보
2) 유일한 식별자에 의해 식별이 되어야
3) 두 개 이상의 영속적으로 존재하는 인스턴스의 집합이어야한다.
..... 등을 고려하는 것이 필요하다. (자세한 사항은 생략)
ex. 고객
Attribute 속성 더 이상 분리되지 않는 최소의 데이터 단위

* 도메인 : 각 속성이 가질 수 있는 값의 범위
  (ex. 성별 : 남자, 여자, 기타)
ex.
고객번호, 성명, 주소
Relationship 관계 상호 관계

- 관계명 ) 관계 시작점과 관계 끝 점이 있다.
- 관계 차수 ) 1 : 1, 1 : M, M : M(Many)
- 관계 선택 사양 ) 필수, 선택
ex.
고객 - 주문하다 - 주문서

 

|  데이터 모델링 프로그램

- ER WIN, ER Studio, eXERD 등 데이터 모델링을 위한 다양한 툴이 있다.

- 그 중에서도 IT업계에서는 ER WIN을 가장 많이 사용한다고 한다.

* ER WIN 다운로드 및 설치 안내 바로가기

* 이클립스 Exerd 플러그인 설치 (더보기)

더보기

[1] Exerd 홈페이지에서 이클립스 플러그인 버전을 선택하면 URL이 제공된다.

    http://exerd.com/update/exerd/3.x/

[2] 이클립스에서 [help]-[install software]를 선택한 후 [Add]를 통해 URL를 입력

 

 

[ 참고 및 출처 ]

부트 캠트 수업을 들은 후 정리한 내용입니다.

데이터모델링 표기법 https://lipcoder.tistory.com/330

https://blog.naver.com/PostView.nhn?blogId=qbxlvnf11&logNo=221225469375 

https://www.sap.com/korea/insights/what-is-data-modeling.html

https://chrismare.tistory.com/entry/ERwin-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-%EB%B0%8F-%EC%84%A4%EC%B9%98

|  VIEW

- 가상의 읽기 전용 테이블

- 장점 :

  - 독립성 : 테이블 구조가 변경되어도 뷰를 쓰는 응용 프로그램은 변경하지 않음

  - 편리성 : 복잡한 쿼리를 뷰로 생성하여 코드를 간결화

  - 보안성 : 계정 권한 수준에 따라 노출되지 않아야 하는 데이터를 숨김 처리 할 수 있음

-- 기본 구문
CREATE VIEW 뷰명 AS
SELECT *
FROM 테이블명

-- 예시
create view v_member as
select
    m.member_type, m.user_id,m.name,
    md.mobile_no, md.marketing_yn, md.register_date
from member as m
    join member_detail md
        on m.member_type = md.member_type and m.user_id = md.user_id
;

 

|  (사용자 정의)함수와 프로시져

  (사용자 정의)함수 프로시져
언제쓰나? 자주쓰는 기능 저장할때 특정 비즈니스 로직을 저장해서 꺼내 쓸 때
공통점 선언부와 구현부가 있는 함수 형태
차이점 반환값 O 반환값 X

A. 함수

-- 기본 구문
CREATE FUNCTION 함수명(매개변수명 타입)
   RETURNS 반환타입
BEGIN
   RETURN
   구현부
END;

-- 예시
-- A. 생성
create function sf_password(password varchar(255))
  returns varchar(255)
begin
  return
  	case 
  	   when length(password) > 2 then 
  	        concat(substring(password, 1, 2), '**')
  	   else '****'
    end;
end;

-- B. 호출
select 
   sf_password(password) as password_mask
from member;

-- C. 삭제
drop function sf_password;

B. 프로시저

-- 기본 구문
CREATE PROCEDURE 함수명()
BEGIN
	구현부
END
;

-- 예시
-- A. 생성
create procedure sp_select_memeber()
begin
    select *
    from member;

    select *
    from member_detail;
end;

-- B. 호출
call sp_select_memeber();

-- C. 삭제
drop procedure sp_select_memeber;

 

C. delimiter를 사용해야 할까?

- 원칙적으로, 함수나 프로시저를 생성할 때, delimiter를 사용하는 것이 필요하다.

  ** delimiter 곧, SQL 명령어 간의 구분은 기본적으로 세미콜론(';')을 통해 이루어지기에,

     SQL 명령어를 구현부에 중첩시키면 하위 명령어는 실행되지 않는 현상이 발생)

- 다만, 툴을 사용하면 delimiter를 사용하지 않아도 오류 없이 잘 실행된다.

delimiter $$  -- delimiter 변경
create procedure sp_select_memeber()
begin
    select *
    from member;

    select *
    from member_detail;
end;
delimiter ;  -- delimiter default인 ;로 원상복구

 

|  트리거

특정 조건이 만족되면 자동으로 실행 시작

rf. 일반적으로 히스토리를 남기는 데에 사용된다.

rf. 상황에 따라 양날의 검이 될 수 있는 명령문이다. (사용에 주의가 필요)

event old new
insert X O
update O O
delete O X
-- RQ. MEMBER 테이블의 핸드폰 번호를 갱신(update)할 때마다 히스토리를 남기고 싶다.

-- A. 변경 사항 담을 히스토리 테이블
create table member_detail_history
(
    id int auto_increment primary key ,
    member_type varchar(10),
    user_id varchar(50),
    mobile_no varchar(50),
    new_mobile_no varchar(50),
    update_date datetime
);

-- B. 트리거 예시
delimiter &&
create trigger tg_member_mobile_no_history
    before update on member_detail -- update 되기 이전에 트리거가 일어난다.
    for each row
    begin
        insert into member_detail_history
        (
          member_type,
          user_id,
          mobile_no,
          new_mobile_no,
          update_date
         )
        values
        (
          old.member_type,
          old.user_id,
          old.mobile_no,
          new.mobile_no,
          now()
        );
    end;
delimiter ;

 

 

[ 출처 ]

- 부트캠프 강의를 들은 후 정리한 내용입니다.

|  ALIAS와 * (애스터리스크)

- ALIAS : 별명, 별칭

- * : 전체

select
    m.id as 회원아이디,
    m.password as 비밀번호,
    m.name as 회원명
from member as m;

 

|  JOIN문

- JOIN만 적히는 경우 INNER JOIN이 사용된다.

INNER JOIN
키 값 기준 데이터 결합
LEFT JOIN
키 값 기준 데이터 결합 + 좌측의 나머지 
RIGHT JOIN
키 값 기준 데이터 결합 + 우측의 나머지
FULL JOIN
LEFT데이터 X  RIGHT데이터

- 수업에서 제공되었던 테이블은, MEMBER와 MEMBER_DETAIL 이었는데,

  이와 같이 회원 정보를 간편 정보 / 상세 정보로 나누어서 필요에 따라 테이블을 조인하는 식으로 사용한다고 한다.

-- 회원정보 > 로그인 정보
create table member
(
   member_type varchar(10) not null comment '회원구분',
   user_id     varchar(50) not null comment '회원 아이디',
   password    varchar(50) null     comment '비밀번호',
   name        varchar(20) null     comment '이름',
   primary key (member_type, user_id)
) comment '회원정보';

-- 회원상세정보 > 로그인에 필요하지 않은 이외 정보
create table member_detail
(
   member_type   varchar(10)                          not null comment '회원구분',
   user_id       varchar(50)                          not null comment '회원 아이디',
   mobile_no     varchar(12)                          null     comment '휴대폰 번호',
   marketing_yn  bit                                  null     comment '마케팅 수신 여부',
   register_date datetime default current_timestamp() null comment '가입일',
   primary key (member_type, user_id),
   constraint fk_member_detail foreign key (member_type, user_id) references member (member_type, user_id)
) comment '회원상세정보';

- JOIN 코드

-- 조인 (INNER JOIN)
-- ** 단순히 select * from.. 을 쓰면 어떤 DBMS에서는 오류가 날 수 있다.
select
    m.*,
    md.*
from member as m
    join member_detail as md
        on m.member_type = md.member_type and m.user_id = md.user_id
;

-- 중복된 컬럼 제외
select
    m.member_type, m.user_id, m.password, m.name,
    md.mobile_no, md.marketing_yn, md.register_date
from member as m
    join member_detail as md
        on m.member_type = md.member_type and m.user_id = md.user_id
;

-- LEFT JOIN : member(left)를 기준으로 member_detail(right)와 키값이 동일한 데이터를 먼저 솎아내고, 그 후에 member의 나머지를 추가
select
    m.member_type, m.user_id, m.password, m.name,
    md.mobile_no, md.marketing_yn, md.register_date
from member as m
    left join member_detail as md
        on m.member_type = md.member_type and m.user_id = md.user_id
;

-- RIGHT JOIN : RIGHT를 기준으로
select
    m.member_type, m.user_id, m.password, m.name,
    md.mobile_no, md.marketing_yn, md.register_date
from member as m
    right join member_detail as md
        on m.member_type = md.member_type and m.user_id = md.user_id
;

-- FULL JOIN : LEFT x RIGHT
select
    m.member_type, m.user_id, m.password, m.name,
    md.mobile_no, md.marketing_yn, md.register_date
from member as m
    join member_detail as md
;

 

|  내장함수

- 각 DBMS별로 내장되어 있는 함수들이 있다.

- 구분

입력값의 갯수에 따라 - 단일행 함수 : 함수의 입력값이 단일행 값일 때
- 다중행 함수 : 함수의 입력값이 다중행 값일 때 (ex. 집계 함수, 그룹 함수)
데이터 타입에 따라 - 문자형 함수
- 숫자형 함수
- 날짜형 함수
- 변환형 함수 : 데이터 타입 변환
- NULL 관련 함수

- SQL 조건문 : CASE ~ END 

-- SQL 조건문 : 자바의 switch case와 동일
CASE 
  WHEN 조건 THEN 실행
  ELSE 실행
END

- 수업에서 다루었던 코드

-- A. 문자형
-- (1) 비밀번호 * 처리 (substring, length, concat)
-- 오라클의 경우, 문자열 결합 시 : concat(문자열, 결합할 문자) 또는 문자열 || 결합할문자
SELECT
    member_type,
    user_id,
    password,
    name,
    -- concat(substring(password, 1, 2), '**') as password_mask, -- idx가 1부터 시작
    -- length(password) as password_length,
    case
      when length(password) > 2 then concat(substring(password, 1, 2), '**')
      else ''
    end as password_mask
FROM member;

-- B. 데이터 포맷 변환
SELECT register_date,
       date_format(register_date, '%Y.%m.%d') as dt_format
FROM member_detail;

SELECT
    '20220321',
    str_to_date('20220321', '%Y%m%d') as dt_date,
    date_add(str_to_date('20220321', '%Y%m%d'), interval 1 month) as dt_date2
FROM dual;

-- 현재 날짜를 통해 월초와 월말을 구하기
SELECT
    date_format(now(), '%Y-%m-%01') as start_date, -- 이번달 첫일
    date_add(date_add(str_to_date(date_format(now(), '%Y-%m-01'), '%Y-%m-%d'), interval 1 month), interval -1 day) as end_date
FROM dual;

- 보다 자세한 내장 함수 코드 

https://2030bigdata.tistory.com/219

 

[개미의 걸음 SQLD 2과목] SQL내장함수① 단일행(문자열, 숫자형, 날짜형, 형변환, NULL)함수

함수[Function] 내장함수[표준함수] SQL에서 기본적으로 내장하고 있어 표준으로 제공하는 함수 사용자 정의함수 SQL에서 사용자가 임의로 만들어서 사용하는 함수 내장 함수[BUILT-IN Function] SQL에서

2030bigdata.tistory.com

 

|  페이징 처리

- 다수의 게시글(또는 카드 등)을 특정 위치에서 특정 갯수만큼 보여주는 것

MySQL, MariaDB LIMIT를 통해 페이징 처리
Oracle ROWNUM을 통해 페이징 처리
MSSQL OFFSET, FETCH를 통해 페이징 처리

📡 MySQL/MariaDB

--!! 주의사항 !! 특히하게 LIMIT은 idx를 0부터 시작한다.

-- 전체 data에 대해 첫번째부터 10개만 출력
SELECT *
FROM member
LIMIT 0, 10; 

-- 나열된 data에 대해 10번째부터 10개만 출력
select
    c.code, c.company_name, c.eng_company_name, c.category,
    row_number() over (order by c.code desc) as row_index
from company c
order by c.code desc
limit 10, 10
;

-- 범위를 지정하여 출력
select *
from
(
    select *
    from
    (
        select
        c.code, c.company_name, c.eng_company_name, c.category,
        row_number() over (order by c.code desc) as row_index
        from company c
        order by c.code desc
    ) t1
    where row_index <= 30
) t2
where row_index > 20;

📡 ORACLE

-- 기본 구문
--- '*'을 사용하면 데이터가 깨진다.
--- 테이블.* 또는 칼럼명을 하나씩 작성 필요.
SELECT
  칼럼1,
  칼럼2,
  ROWNUM as rnum
FROM member
ORDER BY 순서기준

-- 실제 사용
SELECT *
FROM (
		SELECT 
          a.*, ROWNUM as rnum 
        FROM (
                SELECT 
                  * FROM member
                ORDER BY member_no
             ) a
     ) 
WHERE rnum >= 1 AND rnum <= 10;

 

 

[ 참조 및 출처 ]

부트캠프 강의를 들은 후 정리한 내용입니다.

https://programmer93.tistory.com/4

https://m.blog.naver.com/wideeyed/221796538283

|  CRUD

- 소프트웨어가 가지는 기본적인 데이터 처리 기능

- CRUD는 MariaDB/MySQL, ORACLE 모두 동일한 문법을 사용하는 것으로 확인됐다. (아래 코드 비교)

구분 의미 SQL REST API WHERE 필수
CREATE 삽입 INSERT PUT/POST X
READ 조회 SELECT GET X
UPDATE 갱신 UPDATE PUT/PATCH O
DELETE 삭제 DELETE DELETE O
-- CRUD
-- INSERT
INSERT INTO bootcamp_member2
(name, email, mobile_no, password, marketing_yn, register_date)
VALUES
('테스트3', 'test3@gmail.com', '01022223212', '2222', true, now());

-- SELECT : WHERE 선택 (대다수 WHERE 사용)
SELECT *
FROM bootcamp_member2
WHERE marketing_yn = true
  AND name = '테스트3';

-- UPDATE : WHERE 필수
UPDATE bootcamp_member2
SET marketing_yn = false
WHERE name = '김길동';

-- DELETE : WHERE 필수
DELETE FROM bootcamp_member2
WHERE email = 'kim123@gmail.com';

 

1.  데이터 삽입(INSERT)

🐱 단순 데이터 추가

- 아래와 같이 단순 데이터를 추가할 경우, 데이터가 없을 때(NULL) 문제가 발생할 수 있다.
- 회원가입을 예로 들었을 때, 어떤 MEMBER의 ID는 유일해야한다. --> 데이터 입력에 대한 제약이 필요
- 여기서 제약 조건이 필요해진다.
  * 제약조건(Constraint) : 특정 데이터에 대해 거는 제약

📡 MariaDB

INSERT INTO 테이블명
(칼럼명, 칼럼명, 칼럼명....)
VALUES
(값, 값, 값...);

-- 예시
INSERT INTO bootcamp_member
(name, email, mobile_no, password, marketing_yn, register_date)
VALUES
('홍길동', 'simDev1234@gmail.com', '01011111111', '1234', true, now());
더보기

📡 Oracle

INSERT INTO MEMBER(ID, PWD) VALUES('NEW LEC','111');
INSERT INTO MEMBER(ID, PWD) VALUES('DRAGON','111');

🌈 인코딩 문제 해결

- 테이블에 Insert를 하려고 보니 아래와 같은 오류가 나타났었다.

Incorrect string value: '\xED\x99\x8D\xEA\xB8\xB8...' for column ~~~~~~~~

- 내용을 쭉 읽어보니까 위 'name' 칼럼의 한글이 깨져서여서(인코딩 문제) 정확한 원인과 해결 방법을 찾았는데

  - 원인 : DB의 디폴트 인코딩 방식 (default_character_set_name)이 utf8이 아니여서였다.

select schema_name, default_character_set_name from information_schema.schemata;
+--------------------+----------------------------+
| schema_name        | default_character_set_name |
+--------------------+----------------------------+
| information_schema | utf8mb3                    |
| mysql              | latin1                     |
| performance_schema | utf8mb3                    |
| sys                | utf8mb3                    |
| testdb1            | latin1                     |
+--------------------+----------------------------+

  - 해결 : 아래와 같이 db의 인코딩 방식과, 테이블의 인코딩 방식을 변경하면 된다.

  * 한 번 셋팅된 테이블 인코딩 방식은 db 인코딩 방식을 변경해도 유지되므로 꼭 table도 변경해야한다.

alter database 디비명 default character set utf8;
alter table 테이블명 convert to character set utf8;

 

🐱 제약조건을 통한 INSERT

- 아래에서는 제약조건으로 pk를 예시로 들었다.
- 제약조건에 대한 자세한 내용으로는 [더보기]를 참고하면 자세한 내용이 들어있다.

📡 MariaDB

alter table bootcamp_member2 add constraint primary key pk_bootcamp_member2(email);

 

2.  데이터 조회(SELECT)

(1) 선택적 조회 : WHERE절을 쓰자
- 일반적으로 회사에서 데이터를 조회할 때는, 전체 조회를 하지 않는다.
  데이터 양이 방대하기 때문에 컴퓨터나 너무 느려질 수 있기 때문
- 따라서, WHERE절이 필수는 아니라도 함께 써주는 것이 좋다.

(2) and 나 or를 통해 추가적인 조건사항을 걸 수 있다.

- 아래의 코드처럼, where절을 통한 조건을 걸 때 and나 or를 통해 추가 조건을 걸 수 있다.

📡 MariaDB

-- 전체 조회
select *
from 테이블명
;

-- 선택 조회
select 
  name, email
from 테이블명
;

-- where절 조건
select *
from 테이블명
where 조건
   or 조건
;
더보기

📡 Oracle

-- 전체 조회
select * 
from 테이블명
;

-- 선택 조회
select 
   name, kor, eng, mat, prev
from 테이블명
;

 

3.  데이터 갱신(UPDATE)

(1) WHERE절을 필수로 사용해야 한다.
- 그렇지 않은 경우 전체 데이터가 모두 갱신된다.

(2) 여러개의 칼럼을 동시에 갱신할 수 있다.

📡 MariaDB

update 테이블명
set
    변경할 칼럼의 데이터
where 조건
;
더보기

📡 Oracle

update 테이블명
set 
   변경할 칼럼의 데이터
where 조건

 

4.  데이터 삭제(DELETE)

(1) WHERE절을 필수로 사용해야 한다.
- 그렇지 않은 경우 전체 데이터가 모두 삭제된다.

(2) 데이터를 삭제할 때는 주의를 요한다.

📡 MariaDB

delete from 테이블명
where 조건;
더보기

📡 Oracle

delete from scores 
where name = '바길동';

delete from scores 
where 1 = 1; -- 전체 데이터 삭제

 

 

[ 출처 및 참고 ]

- 부트캠프 수업 내용을 들은 후 정리한 내용입니다.

- 과거에 정리했던 내용과 함께 정리

https://why-dev.tistory.com/73?category=958782 

+ Recent posts