길고 긴 방황을 끝내고 회사에 첫 출근을 했다.

앉아서 팀장님과 커피챗을 나누고 있는데, 마이바티스에 대한 이야기가 나왔다.

"저는 프로젝트 할 때 ORM으로 마이바티스 대신 JPA를 써서요" 라고 했더니 돌아오는 답변

"마이바티스는 ORM이 아닌데?"

"?????"

백엔드 공부를 얼마 하지 않은 입장에서 [프레임워크 + 프로젝트 관리 도구 + ORM]의 한 세트를

'Spring + Maven + MyBatis'와 'Spring Boot + Gradle + JPA'로 이해하고 있었다.

 

글을 읽는 사람도, 나도 하도 많이 들어서 다음 3가지는 알고 있을 것이다.

- JPA는 자바 진영의 ORM 표준이고, 이를 구현한 것이 Hibernate이다.

- ORM은 OOP를 지향하는 언어와 DB의 relation을 맺어주는 기법이다.

- Gradle은 Maven보다 현저히 빌드 속도가 빠르고 스크립트가 짧다.

 

이렇게 현대의 김영한님 강의를 듣고 자란 사람들이나 국비교육이 아닌 사비 부트캠프를 다닌 사람은 보통 jpa, gradle, spring boot에 대해서는 이해도가 높지만, 반대로 maven+mybatis를 이해하는 경우는 잘 없다.

 

일단 mybatis는 ORM이 아니다. myBatis는 SQL 매핑 프레임워크다.

JPA와 비슷하지만, 객체를 매핑해준다기보다 SQL안에 객체의 프로퍼티들을 잘 넣을 수 있게 한다.

JPA도 동적 쿼리를 지원하지만, myBatis는 조금 더 세밀한 쿼리 튜닝을 가능하게 한다.

 

다음에는 myBatis를 이용한 쿼리튜닝을 포스팅을 해야겠다.

 

 

'DB' 카테고리의 다른 글

DOCKER로 MySQL 다루기  (0) 2023.08.24
POSTGRESQL - RDB  (0) 2023.08.19
POSTGRESQL - 분노의 설치  (0) 2023.08.19

데이터베이스와 테이블의 차이는 무엇인가요?

우리가 가장 관심있어하는 정보는 테이블 안에 있고, 이 테이블 안의 여러 데이터를 제어하는 것이 우리가 가장 하고 싶어하는 일이다. 이 테이블을 묶어서 그룹핑하고, 이름을 붙인 것이 스키마이고, 이 스키마를 관리하기 위한 것이 데이터베이스이다. 여러개의 데이터베이스를 묶은 것을 클러스터라고도 하고 DBMS라고도 하는데, 이 DBMS를 데이터베이스라고 부르기도 한다.

MySQL에서 데이터를 조회할 때 사용하는 기본 SQL 명령어를 설명해주세요

  • SELECT
    • SELECT절은 여러 조건들을 처리한 후 남은 데이터에서 어떤 ROW를 출력할 지 선택하는 역할을 한다.
  • WHERE
    • WHERE절은 FROM절에서 읽어온 테이블에서 조건에 맞는 결과만 갖도록 데이터를 필터링한다.
  • FROM
    • 조회하려는 전체 테이블의 정보를 가져온다.
  • HAVING
    • HAVING절은 그룹핑 후 각 그룹에 사용되는 조건절로, HAVING절은 각 그룹에 조건을 걸기 때문에 퍼포먼스가 떨어지는 단점이 있다. 그래서 WHERE에 조건을 쓸 수 있다면 WHERE에 조건을 쓰는게 좋다.
  • JOIN
    • JOIN절은 2개나 그 이상의 테이블을 연결하고, 연결한 테이블로부터 필요한 열을 조회할 수 있도록 한다.
  • ORDER BY
    • ORDER BY절은 출력할 ROW를 어떤 순서에 맞게 보여줄 지 결정한다.

SQL 명령문의 실행 순서는?

  1. 먼저 FROM절에서 조회하려는 전체 테이블의 정보를 가져온다. JOIN이 있다면 다른 테이블과 조인하여 필요한 정보를 가져온다.
  2. WHERE절로 FROM절에서 조회한 테이블에서 필요한 데이터를 필터링한다.
  3. GROUP BY로 선택한 칼럼으로 그룹핑한다.
  4. HAVING으로 그룹핑한 각 그룹에서 조건을 걸어 다시 필터링한다.
  5. SELECT절로 여러 조건들을 처리한 후의 데이터를 가져온다.
  6. ORDER BY를 이용하여 SELECT문에서 가져온 ROW를 어떤 순서에 맞게 보여줄 지 정한다.
  7. LIMIT로 보여줄 ROW의 개수를 정한다.

'Primary Key'와 'Foreign Key'의 차이와 각각의 역할에 대해 설명해주세요.

PK

  • Primary Key는 데이터베이스 테이블의 각 레코드를 고유하게 식별하는 역할을 합니다.
  • 한 테이블에는 하나의 PK만 존재할 수 있으며, 이 PK는 해당 테이블의 모든 레코드에 대해 고유해야 합니다.
  • PK는 NULL이 될 수 없습니다.
  • PK로 지정된 컬럼은 클러스터링의 기준으로 사용되어, 데이터가 저장되는 방식을 결정합니다.

FK

  • Foreign Key는 다른 테이블의 PK를 참조하여 두 테이블 간의 관계를 설정하는 역할을 합니다.
  • FK로 지정된 필드 혹은 필드 셋의 값은 참조하는 PK에 있는 값 중 하나여야 합니다. 이 규칙은 참조 무결성의 제약 조건을 만족시키기 위한 것입니다.
  • FK는 NULL일 수도 있으며, 참조하는 PK에 없는 값을 가질수도 있습니다.

차이점

  • PK는 한 테이블 내에서 각각의 ROW / RECORD를 고유하게 식별하기 위해 사용되며, FK는 한 테이블과 다른 테이블의 관계를 구축하기 위해 사용됩니다.
  • PK는 같은 컬럼 안의 모든 레코드가 UNIQUE해야하는 반면 FK는 같은 컬럼 안의 레코드가 중복될수도 있고, NULL이나 참조하는 PK의 ROW에 해당하지 않는 값을 가질수도 있습니다.

MySQL에서 'JOIN'이란 무엇이며, 'INNER JOIN'과 'LEFT JOIN'의 차이점은 무엇인가요?

JOIN은 두 개 이상의 TABLE에서 ROW를 결합하는 데 사용되는 연산이며, 주로 테이블 간에 관계를 맺고 있을 때 사용됩니다. JOIN의 형태는 여러개지만, 가장 기본적인 형태는 INNER JOIN과 LEFT JOIN입니다.

INNER JOIN

  • INNER JOIN은 두 테이블 간의 교집합을 반환합니다.
  • 즉, 조인 조건에 일치하는 레코드만 결과로 반환합니다.
  • 예를 들어 A라는 테이블과 B라는 테이블이 있어서 INNER JOIN을 하면, A와 B 모두에 들어있는 데이터만 반환합니다.

LEFT JOIN(LEFT OUTER JOIN)

  • LEFT JOIN은 첫 번째 테이블의 모든 레코드와 일치하는 두 번째 테이블의 레코드를 반환합니다.
  • 오른쪽(두 번째) 테이블에서 일치하는 값이 없을 경우 NULL을 반환합니다.

정규화(Normalization)란 무엇이며, 왜 중요한가요?

NORMALIZATION은 테이블의 각 레코드의 중복을 최소화하고, 데이터 구조를 효율적으로 만드는 과정입니다. NORMALIZATION을 통해 데이터의 무결성(INTEGRITY)와 일관성(CONSISTENCY)을 유지할 수 있습니다.

정규화의 단계는 1NF, 2NF, 3NF 등이 있지만 너무 많은 정규화를 사용할 경우 성능하락이 동반될 수도 있으므로 성능과 무결성 사이에서 적절한 균형을 찾아야 합니다.

'DB > SQL' 카테고리의 다른 글

SQLD - Alter시 Oracle, MySQL, MSSQL의 차이  (0) 2023.02.13

Database에 대한 추가 이해가 필요해 MySql 수업을 들어야 했다.

내 PC에는 MySql Workbench가 설치되어 있었지만,

마침 Docker 수업을 들었었기에 이를 이용하여 Cli로 MySQL을 다뤄보기로 했다.

기본적인 docker container를 어떻게 실행하는지에 관해서도 포스팅했다.

https://choincnp.tistory.com/70

 

DOCKER에서 MySQL 컨테이너 띄우기

먼저, docker hub에서 mySQL 관련 image를 찾는다.

그러면 첫 번째로 mysql에 대한 official image가 있고, 들어가보면 명령어를 찾을 수 있다.

먼저 Docker desktop을 실행시키고, mysql 이미지를 가져오자.

$ docker pull mysql

설치가 되는 것을 볼 수 있다. 잘 설치되었는지 한번 확인해보자.

$ docker images
REPOSITORY                                                             TAG          IMAGE ID       CREATED       SIZE
mysql                                                                  latest       99afc808f15b   13 days ago   577MB

이제 이 이미지를 기반으로 한 컨테이너를 띄울 것이다.

$ docker run --name mysql-container -e MYSQL_ROOT_PASSWORD=<password> -d -p 3306:3306 mysql:latest

다시 설명하자면, 이 명령어를 자연어로 번역하면 다음과 같다.

mysql:latest 이미지를 기반으로 하는 mysql-container라는 이름을 가진 container를 띄울 거야.
-e라는 옵션으로 environment variable(환경 변수)를 줄 건데, MYSQL_ROOT_PASSWORD는 로 줄거야.
-d라는 옵션으로, 백그라운드에도 이 컨테이너를 계속 돌릴거야. (dispatch)
-p라는 옵션으로, host OS의 3306번 포트와 컨테이너의 3306번 포트(mySql 기본 포트)를 포트포워딩 해줘.

<password>에는 꼭 기억하기 쉬운 패스워드를 넣도록 하자.

이제, 설치가 되었는지 확인하자.

$ docker ps
CONTAINER ID   IMAGE          COMMAND                   CREATED         STATUS         PORTS                               NAMES
47d2686d7c9b   mysql:latest   "docker-entrypoint.s…"   7 minutes ago   Up 7 minutes   0.0.0.0:3306->3306/tcp, 33060/tcp   mysql-container

잘 설치가 되었음을 확인할 수 있다.

이제 다음 명령어를 한 번에 실행해보자.

  1. Container를 띄워서 bash shell을 실행하고,
  2. mysql root 계정에 접속해보자.
$ docker exec -it mysql-container bash
bash-4.4# mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 15
Server version: 8.1.0 MySQL Community Server - GPL

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql >

query를 입력할 수 있는 라인이 뜨면, 정상적으로 접속한 것이다.

default로 생성된 DB를 확인해보자.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)

총 4개의 DB가 디폴트로 생성되어 있음을 확인할 수 있다.

이제, 우리가 사용할 database를 만들어보자.

나는 회사와 관련된 스키마를 만들고 테스트 할 것이므로, company라는 DB를 만들었다.

mysql> CREATE DATABASE company;

Query OK, 1 row affected (0.00 sec)

mysql> SHOW DATABASES;

+--------------------+
| Database           |
+--------------------+
| company            |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

이제, 이 company를 이용해야 하므로 다음과 같은 명령어를 준다.

mysql > use company;

이제, 테이블을 만들어보자.

mysql> create table DEPARTMENT(
    -> id INT PRIMARY KEY,
    -> name VARCHAR(20) NOT NULL UNIQUE,
    -> leader_id INT
    -> );
Query OK, 0 rows affected (0.05 sec)

세미콜론(;)을 넣지 않으면 자동으로 shell에서 줄바꿈을 지원한다.

처음으로는 '부서(department)'에 관한 테이블을 만들었다.

mysql> show tables;
+-------------------+
| Tables_in_company |
+-------------------+
| DEPARTMENT        |
+-------------------+
1 row in set (0.00 sec)

테이블이 잘 만들어진 것을 확인할 수 있다.

그렇다면, 추가적으로 workbench에서도 이 내용이 반영되었을까?

WORKBENCH에서 DATABASE 확인하기

docker-mysql이라는 커넥션을 만들고 연결한 다음,

아까 host의 3306번 포트와 container의 3306번 포트를 연결시켰으므로 포트에 3306 옵션을 주고 들어간다.

쿼리 콘솔에서 select문으로 테이블을 확인해보면, 다음과 같이 잘 만들어졌음을 알 수 있다.

그럼 반대로, workbench에서 실행한 내용도 container에 잘 들어 갈까?

내가 결국 만들 db는 위와 같은 형태를 띄고 있으므로,

다음은 employee TABLE을 만들어보자.

CREATE TABLE EMPLOYEE(
 id INT PRIMARY KEY,
 name VARCHAR(30) NOT NULL,
 birth_date DATE,
 sex CHAR(1) CHECK(sex in ('M', 'F')),
 position VARCHAR(10),
 salary INT DEFAULT 5000000,
 dept_id INT,
 FOREIGN KEY(dept_id) references DEPARTMENT(id)
  on delete SET NULL on update CASCADE,
 CHECK(salary >= 5000000)
);

QUERY CONSOLE에서 위 SQL을 입력했고, 테이블까지 잘 만들어졌다.

이제 CLI에서 내용이 잘 실행되었는지를 확인해보자.

mysql> show tables;
+-------------------+
| Tables_in_company |
+-------------------+
| DEPARTMENT        |
| EMPLOYEE          |
+-------------------+
2 rows in set (0.00 sec)

잘 적용되었음을 확인할 수 있다. 과연 내용들도 잘 만들어 졌을까?
DESC(DESCRIBE) TABLE 명령어로 테이블 구조나 attributes를 확인할 수 있다.

mysql> DESC EMPLOYEE
    -> ;
+------------+-------------+------+-----+---------+-------+
| Field      | Type        | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| id         | int         | NO   | PRI | NULL    |       |
| name       | varchar(30) | NO   |     | NULL    |       |
| birth_date | date        | YES  |     | NULL    |       |
| sex        | char(1)     | YES  |     | NULL    |       |
| position   | varchar(10) | YES  |     | NULL    |       |
| salary     | int         | YES  |     | 5000000 |       |
| dept_id    | int         | YES  | MUL | NULL    |       |
+------------+-------------+------+-----+---------+-------+
7 rows in set (0.00 sec)

 

이렇게 mySQL을 docker에서 돌리는 방법을 확인했다. 정말 별 것 아니다.

 

오늘도 열심히 따라와준 나에게도 박수 :)

'DB' 카테고리의 다른 글

ORM이란 무엇인가요? JPA와 MyBatis는 어떤 차이가 있나요?  (0) 2023.12.13
POSTGRESQL - RDB  (0) 2023.08.19
POSTGRESQL - 분노의 설치  (0) 2023.08.19

모든 데이터는 DB 위에서 움직인다.

그만큼 DB는 어플리케이션의 많은 부분을 차지하고, 또 중요한 역할을 맡는다.

그 중, 2023년 현재는 아직도 RDB의 전성시대이며, 그 중 Oracle, MySQL, MsSQL에 이어

4위를 차지하고 있는 PostgreSQL은 왜 사용하고 있는지 알아보고, 어떻게 사용하는 지 알아보자.

Oracle, MySQL이라는 거함이 있고, MongoDB같은 NoSQL이 아님에도 불구하고,

PostgreSQL은 어떤 장점이 있어서 사용하는 것일까?

Chat GPT는 다른 rdbms에 비해 PostgreSQL이 가진 장점을 다음과 같이 요약한다.

  1. 고급 기능 및 SQL 규정 준수: PostgreSQL은 풍부한 고급 기능 세트와 높은 수준의 SQL 규정 준수로 유명합니다. 많은 고급 데이터 유형, 인덱싱 방법 및 전체 텍스트 검색, JSON 지원 및 지리적 데이터 유형과 같은 기능을 지원합니다. 프로젝트에 복잡한 쿼리가 필요하거나 특수 데이터 유형으로 작업해야 하는 경우 PostgreSQL이 더 나은 선택일 수 있습니다.
  2. ACID 준수: PostgreSQL은 ACID(Atomicity, Consistency, Isolation, Durability) 준수를 강조합니다. 강력한 트랜잭션 지원을 제공하며 이는 정확한 데이터 무결성과 안정성이 필요한 애플리케이션에 중요할 수 있습니다.
  3. 확장성: PostgreSQL의 확장 시스템을 사용하면 사용자 정의 함수, 연산자 및 데이터 유형을 추가할 수 있습니다. 이는 특정 애플리케이션 요구 사항에 맞게 데이터베이스 기능을 확장해야 할 때 유리할 수 있습니다.
  4. 동시성 제어: PostgreSQL의 동시성 제어 처리는 종종 찬사를 받습니다. MVCC(Multi-Version Concurrency Control)를 사용하여 서로를 과도하게 차단하지 않고 여러 트랜잭션이 동시에 발생할 수 있도록 합니다. 이것은 높은 수준의 동시 읽기 및 쓰기 작업이 있는 애플리케이션에 특히 유용할 수 있습니다.
  5. 복잡한 쿼리에 대한 성능: 여러 조인 또는 대규모 데이터 세트와 관련된 복잡한 쿼리가 있는 시나리오에서 PostgreSQL의 쿼리 최적화 프로그램은 종종 보다 정교하고 효율적인 쿼리 계획을 생성할 수 있는 것으로 간주됩니다.

ACID를 지키거나 MVCC같은 경우는 다른 rdbms도 지원하고 있으므로,

MySQL이나 Oracle보다 조금 더 복잡한 쿼리를 효율적으로 다룰 수 있고, 복잡한 데이터도 특수 데이터로 다룰 수 있는것이 장점인 모양이다.

 

그럼 PostgreSQL을 설치해보자. 설치 방법 및 오류는 아래 포스트에 작성했다.

https://choincnp.tistory.com/76

 

POSTGRESQL - 분노의 설치

postgreSQL을 설치하여 사용하려다 너무 많은 error를 만나서 따로 포스팅한다. 다음 설치법은 윈도우 10 기반이며, 각 윈도우 버전마다 만나는 에러가 다를 수 있다. postgresql.org에서 download에 들어가

choincnp.tistory.com

설치할 때 다음과 같은 화면을 봤을 것이다. 각 옵션들에 대해 알아보자.

  • PostgreSQL Server
    • 데이터를 보관하고 관리하는 프로그램, 대부분의 일을 한다.
    • client는 보통 Server의 데이터에 직접 접근할 수 없으므로 아래 2개의 클라이언트를 이용해서 PostgreSQL Server와 소통한다.
  • pgAdmin4(GUI)
  • Command Line Tools(CLI, psql)
    • 이 클라이언트를 이용해 서버가 다른 컴퓨터에 있더라도, 원격으로 접속하여 데이터를 다룰 수 있다.
  • Stack Builder
    • postgreSQL과 관련된 여러 추가적인 파일을 설치할 수 있게 해준다.

DB는 어떻게 구성되어 있을까?

  • 먼저, 우리가 가장 관심있어 하는 Table이 있다. 이 테이블 안의 여러 데이터를 제어하는 것이 우리가 가장 하고싶은 일이다.
  • 연관된 테이블을 묶어서 그룹핑하고, 이름을 붙인 것이 바로 Schema다.
  • Database는 많은 스키마들을 묶어서 관리한다.
  • 마지막으로, 이 많은 데이터베이스들을 묶은 것이 바로 Cluster(database server)다.

결국 이 클러스터라는 것이 데이터베이스의 실체이다. 그래서 클라이언트는 결국 '클러스터'와 접속을 시도하게 되는 것이다. 그리고 마지막으로 우리는 SQL(Structured Query Language)을 통해 특정 테이블을 제어하는데, 이 과정은 다음과 같다.

1. 특정 DB 서버(클러스터)에 접속한다.
2. DB를 선택한다.
3. Schema를 선택한다.
4. Table을 대상으로 하는 명령어를 날린다.
5. 그리고 이 DB 서버는 요청한 작업의 결과를 다시 클라이언트에 보낸다.

 

여기까지 우리가 배운것을 정리하면

  • postgreSQL이란 무엇인가?
  • postgreSQL은 다른 rdbms와 비교해 어떤 차이점이 있는가?
  • db는 어떻게 구성되어 있는가?
  • db의 테이블을 제어하는 과정은 어떻게 되는가?

이다. 꼭 대답할 수 있어야 한다.

 

이제 조금전에 설치한 postgreSQL을 사용해보자.

아까 설치한 GUI인 pgAdmin을 실행해서 좌측 메뉴인 Servers를 누르면 password가 나온다.

설치할 때 넣은 비밀번호를 넣고 들어가보자. PostgreSQL 15라는 default database가 여러분들을 반길 것이다.

이제, 본격적으로 클러스터를 만들어보자.

PostgreSQL15 > Register > Server

처음 General 화면에서는 db이름을 설정해두고, connection으로 넘어오자. 나는 MyPG라는 이름을 주었다.

  • Host name에서는, 우리는 클라이언트와 서버가 같은 환경에 있으므로 Localhost를 써주자. 127.0.0.1을 써 줘도 된다.
  • Port는 기본적으로 PostgreSql server가 5432번 포트에서 리스닝하고 있기 때문에 그대로 놔 두어도 된다. 나머지 옵션도 그대로 두자. password는 아까의 password 그대로 넣자.

그리고 save버튼을 누른다. 그러면 코끼리 모양의 MyPG 클러스터가 생성된 것을 볼 수 있을 것이다.

db가 만들어짐과 동시에 맨 밑의 파란색 사람모양의 postgres는 슈퍼 사용자가 만들어짐을 볼 수 있다.

이제 Database Server를 만들었으니, 하위 개념인 database를 만들어보자.

SQL을 누르면 우리의 db가 어떤 SQL syntax로 생성되고 있는지를 알 수 있다.

db를 만들면 디폴트로 public이라는 스키마가 생성된 것을 알 수 있다.

이제, 우리가 최종적으로 하고 싶었던 table을 만들어 보자.

tables에서 create를 선택하고 columns를 들어가면 row를 만들 수 있다.

이제 테이블 설정을 다 했으니, SQL로 우리가 한 설정이 어떻게 되었는지를 한번 보자.

이제, 테이블에 데이터를 삽입해보자.

먼저, 우리가 만든 테이블에서

view Data를 누르고,

Add row를 누르면 다음과 같은 화면이 등장한다.

여기의 id는 PK이므로 건들지 말고, title과 body에 데이터를 넣고 save를 해보자.

https://postgrescheatsheet.com/#/tables

에서 cheet sheet를 통해 여러가지 명령문을 배워보고, 쿼리 콘솔에서 이런저런 명령어를 써보자.

이 글에서 자세한 SQL syntax는 다루지 않겠다.

이렇게 간단하게 postgreSQL에 대해 알아보았다.

 

다음에는 EC2를 이용해서 Docker에 postgreSQL을 넣고 cli에서 이걸 다루어보려고 한다.

'DB' 카테고리의 다른 글

ORM이란 무엇인가요? JPA와 MyBatis는 어떤 차이가 있나요?  (0) 2023.12.13
DOCKER로 MySQL 다루기  (0) 2023.08.24
POSTGRESQL - 분노의 설치  (0) 2023.08.19

postgreSQL을 설치하여 사용하려다 너무 많은 error를 만나서 따로 포스팅한다.

다음 설치법은 윈도우 10 기반이며, 각 윈도우 버전마다 만나는 에러가 다를 수 있다.

postgresql.org에서 download에 들어가면, download the installer버튼을 눌러서 edb에서 제공하는 맞춤 다운로드로

OS에 맞는 최신 버전의 postgreSQL을설치할 수 있다.

그러나, windows에서 이 edb에서 제공하는 postgreSQL 설치 파일을 실행하다보면, 다음과 같은 오류를 확률적으로 만날 수 있다.

윈도우에서 설치할 때 이와같은 에러가 뜨는 이유는,

비정상적인(?), 혹은 확인이 되지 않은 Application을 설치하는데 있어서 관리자 권한이 부족하기 때문이다.

오랜 시간 구글링을 한 결과, 이 에러창을 해결하기 위해서는 몇 가지 방법을 찾을 수 있었다.

 

1. stackoverflow에 나와있는 11버전으로 다운로드

https://get.enterprisedb.com/postgresql/postgresql-11.2-1-windows-x64.exe

2. 블로그 참고하여 administrator 옵션 주고 다운로드

https://heeonii.tistory.com/8

3. 바이너리 파일을 이용하여 실행

https://ssjeong.tistory.com/entry/DB-Windows10-PostgreSQL-Binary-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0-Portable

 

나는 15버전을 설치하고 싶었어서 1번 옵션은 제꼈고,

윈도우 특성 상 디렉토리에 대문자가 들어가거나 '-'와 같은 문자가 들어가면 cmd나 shell에서 루트를 인식하지 못해서

2번도 설치하다가 취소했다.

결국 3번의 블로그를 참고하여 바이너리 파일을 이용하여 설치하고 실행하려고 했으나,

EC2도 아니고 Host OS에서 어플리케이션을 설치하는데, 그래도 GUI를 이용해서 제어해보고 싶은 마음에

바이너리 파일을 설치해서 서비스로 등록하고 실행까지 돌렸다가, 2번을 이용해서 다른 방법으로 설치를 해 보고 싶어졌다.

 

먼저, cmd를 켜서 다음과 같은 명령어를 입력한다.

net user Administration /active:yes

이 명령어를 사용할 경우, 윈도우 사용자에 forced하게 install할 수 있는 Administration 계정이 나타나게 된다.

그래서 Ctrl + Alt + Delete를 눌러서 사용자 전환을 들어간 후 Administration계정으로 접속하고,

다시 postgresql.org > download로 host의 file system에 설치 파일을 다운로드 해서 실행하면,

짜잔, 이제는 설치 위자드가 열렸다. 이제 다른 블로그들과 마찬가지로 설치를 해 주면 된다.

설치를 마치고, 다시 다음과 같은 명령어를 입력해주면 로그온 메뉴에서 Administration 사용자를 안 봐도 된다.

net user Administration /active:no

 

여기서 확인해야 할 것은, 만약 바이너리 설치 등 어떤 이유에서라도 5432번 포트를 쓰는 조건을 걸었다면,

다른 포트에서 리스닝을 시켜줄 수 있도록 빈 포트를 선택해야 한다.

나는 설치 파일이 열린것에 너무 흥분해서 자동적으로 5433번 포트가 설정되어 있는지도 모르고,

당연히 5432번 포트에서 listen을 시켜줄 것이라고 생각했는데, 이러면 나중에 큰 화를 맞이하게 된다.

 

이렇게 설치했으면 정상적으로 postgreSQL을 사용하면 되고,

나처럼 5433번 포트를 사용한 경우에는

추후 connection에서 Port번호를 변경해주면 된다.

지금 내가 어떤 포트에서 리스닝 시켜줬는지 확인하는 방법은 간단하다.

1. 기본 클러스터에서 포트번호 확인

처음에 servers에 들어오면 'postgreSQL 15'라는 DB server가 자동으로 설치되어 있다.

이 서버의 properties를 확인해보면, Connection 탭에서 Port번호를 확인할 수 있다.

 

2. 파일 설정에서 포트번호 확인

설치된 폴더 디렉토리(~postgreSQL\15\data)에 가보면, postgresql.conf라는 configuration파일이 존재한다.

이 파일을 열어서 port를 보면 기본 포트가 어떻게 세팅되어있는지를 확인할 수 있다.

 

만약, 포트 번호가 다르다면 

다음과 같이 커넥션 타임아웃이 뜨면서 왜 뜨는지도 모를 지옥같은 오류해결시간이 시작된다.

 

 

* 참고한 블로그 목록

 

'DB' 카테고리의 다른 글

ORM이란 무엇인가요? JPA와 MyBatis는 어떤 차이가 있나요?  (0) 2023.12.13
DOCKER로 MySQL 다루기  (0) 2023.08.24
POSTGRESQL - RDB  (0) 2023.08.19

개괄

JPA 강의를 듣다 보면 나처럼 두뇌가 느리게 굴러가는 사람들은 이상한 결론에 도달할 것이다.

"음... Querydsl만 있으면 jpa를 완벽 해결할 수 있겠구만"

"음... fetch join만 사용하면 join과 관련된 최적화 이슈를 다 해결할 수 있겠구만"

맞는 말이긴 하다.

아마 "Spring Boot를 배워놓으면 Spring은 배우지 않아도 될거야, Spring Boot가 다 해결해주니까!"와 비슷한 개념일 것이다.

 

보통 N+1 문제를 해결하기 위해

ManyToOne을 Lazy Loading으로 걸어주고 페치 조인으로 추가쿼리가 나가지 않게끔 해결하곤 한다.

그러나 이게 습관이 되면 그냥 다 fetch join을 걸어준다.

그러나 fetch join은 연관된 정보를 미리 다 땡겨옴으로서 오히려 단순 조회에서는 성능이 더 안 나올때도 있다.

예를 들어 다음의 엔티티 테이블이 있다고 생각해보자.

 

[Project] 1 : N [Project_User] N : 1 [User]

 

Project와 User의 다대다 관계를 해결하기 위해 일대다 관계로 두번 나누었고, 행위엔티티를 기록하기 위해 Project_User라는 테이블을 만들어 주었다.

 

그리고 User에 있는 id와 연관된 모든 project를 가지고 오고 싶다고 생각해보자.

JPQL로 나타내면

"select p from Project p join p.projectUserList pu where pu.user.userId = :userid"일 것이다.

그러나 나는 여기에 join fetch를 걸어 주어서 다음과 같은 JPQL문이 나가게 되었다.

"select p from Project p join fetch p.projectUserList pu where pu.user.userId = :userid"

join fetch를 걸면 쿼리가 2방이 나가게 된다. 살펴보자.

Hibernate: 
    select
        ...
        projectuse1_.project_id as project_2_5_1_,
        projectuse1_.user_id as user_id3_5_1_,
        projectuse1_.project_id as project_2_5_0__,
        projectuse1_.id as id1_5_0__ 
    from
        project project0_ 
    inner join
        project_user projectuse1_ 
            on project0_.project_id=projectuse1_.project_id 
    where
        projectuse1_.user_id=?
Hibernate: 
    select
        ...
    from
        users user0_ 
    where
        user0_.user_id=?

project에 대한 정보와 projectuser에 대한 정보를 select하는 쿼리와 동시에 projectuser까지 전부 조회해버리는 쿼리가 나가서 결국 user에 대한 정보까지 긁어오는 것을 볼 수 있다.

그러나 user까지 조회할 것이 아니라면 단순히 join만 걸어주면 쿼리를 훨씬 절약하게 된다.

join만 걸고 나가는 쿼리는 다음과 같다.

Hibernate: 
    select
        ...
    from
        project project0_ 
    inner join
        project_user projectuse1_ 
            on project0_.project_id=projectuse1_.project_id 
    where
        projectuse1_.user_id=?

 

단순히 프로젝트에 대한 정보만 가지고 오는 것을 볼 수 있다.

물론 이 조회에서 더 나아가서 user info까지 말아서 데이터를 전송해야 하는 입장이면 join fetch가 맞다.

그러나 프로젝트에 대한 정보만 조회하는데 굳이 쿼리를 한 번 더 쓸 일은 아닌 것 같다.

 

결국 Join과 Fetch join에 대한 이해가 있어야 최적화도 가능하다.

 

뭐든지 알고 쓰자.

'DB > JPA' 카테고리의 다른 글

QueryDsl Custom Repository 명명 규칙  (0) 2023.03.31
왜 GenerationType.Identity를 써 주는 것일까?  (0) 2023.02.23

개괄

프로젝트 내의 jpql로 이루어진 조회 / 수정 쿼리들을 Querydsl을 이용한 쿼리로 migration하는 날

오늘 갑자기 CQS 원칙을 지키고 싶어져서 다음과 같이 Repository를 만들어 주었다.

 

repository

|-ProjectRepository

|-ProjectViewRepositoryCustom

|-ProjectViewRepositoryImpl

 

그리고 ProjectRepository 안의 userId로 projectResponseDto를 찾는 다음과 같은 쿼리도 변경해주어야 했다.

- ProjectRepository

@Query("select new com.ddalggak.finalproject.domain.project.dto.ProjectBriefResponseDto(p.projectId, p.projectTitle, p.thumbnail) from Project p join p.projectUserList pu where pu.user.userId = :userId")
List<ProjectBriefResponseDto> findAllinDtoByUserId(Long userId);

-ProjectViewRepositoryCustom

List<ProjectBriefResponseDto> findProjectAllByUserId(Long userId);

-ProjectViewRepositoryImpl

@Override
	public List<ProjectBriefResponseDto> findProjectAllByUserId(Long userId) {
		return queryFactory.select(new QProjectBriefResponseDto(
				project.projectId,
				project.projectTitle,
				project.thumbnail
			))
			.from(project)
			.join(project.projectUserList, projectUser)
			.where(projectUser.user.userId.eq(userId))
			.fetch();
	}

분명히 연관관계도 문제가 없었고, 모든게 잘 되었다고 생각했는데 뜻하지 않은 오류를 만났다.

 

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'projectController' defined in file [~/ProjectController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'projectService' defined in file [~/ProjectViewRepositoryCustom.findProjectAllByUserId(java.lang.Long)! No property 'userId' found for type 'Project'

ProjectViewRepositoryCustom 내부에 있는 findProjectAllByUserId 메소드의 파라미터로 받는 userId가 Project 내부에 있지 않아 UnsatisfiedDependencyException이 걸린 것이었다.

 

아니 그럼 Querydsl에서는 join문 못쓰나? 라고 생각해봤지만 그럴 리가 없었다.

그렇다고 user의 id값을 projectdto에 넣어주는것도 말이 되지 않았다.

그래서 Project 안에 있는 ProjectUser라는 행위 엔티티에서 User를 뽑아내어 어떻게든 UserId와 연결지어주려는

장장 3시간의 혈투가 시작되었다.

 

join문을 걸었다가 inner join을 걸었다가 join on을 걸었다가 참 많은 삽질 끝에 메소드 구현체를 지워봤는데도 똑같은 오류가 떴다.

 

결국

List<ProjectBriefResponseDto> findProjectAllByUserId(Long userId);

이 문장에서 오류가 있던 것이었는데, 이걸 어떻게 고치나 열심히 고민하다가 결국 흑마법을 사용하기로 결심했다.

그래서 혹시 Project와 Repository의 연결에 문제가 있는게 아닐까 싶어서

ProjectViewRepositoryCustom -> ProjectRepositoryCustom으로 리팩터링해봤더니 된다...

 

또, 과거에는 ProjectRepositoryCustom을 상속받으려면 ProjectRepositoryImpl이라고 했어야 했는데, 이제 ~CustomImpl도 가능하다.

 

결론

다음과 같이 명명을 해 주어야 한다.

  • Custom하려는 Repo는 상속받을 Repository와 이름이 같아야 한다.
    • 만약 CQS를 시행하고자 했으면 조회를 위한 Repository를 ProjectViewRepository로 따로 만들어 주어야 한다.
  • 이제는 Custom Repo를 구현하는 Impl class에는 CustomImpl로 명명해 이름에 일관성이 있게 하자.

'DB > JPA' 카테고리의 다른 글

Join과 fetch join, 알고 쓰자.  (0) 2023.03.31
왜 GenerationType.Identity를 써 주는 것일까?  (0) 2023.02.23

보통 JPA를 이용할 때 인강을 들으면 무조건

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

이 두개를 써준다. 왜 써줄까?

먼저 @Id에 대한 설명을 보자.

@Id를 붙이면 JPA에서 자동으로 PK라고 인식을 해 준다. 그리고 래퍼 타입, date class, bigdecimal 등으로 써 주어야 함을 강제한다.

@GeneratedValue를 써 주면 우리가 ++sequence를 해 주지 않아도, Auto-Increment 덕에 알아서 하나씩 올라간다.

Generation Type에는  Identity, Auto, Sequence등이 있는데, 보통은 겹치지 않는 Identity를 사용한다.

Auto를 사용하게 되면 다음과 같은 일이 일어난다.

게시글, 댓글, 댓글 순으로 게시물을 저장할 경우 우리는 보통

게시글1, 댓글1, 댓글2로 가져가길 원하지만, Auto 타입을 주면

게시글1, 댓글2, 댓글3이라는 문제가 일어난다.

 

나중에 테이블 전략과 db의 입장에서 다시 설명하기로 하고, 오늘은 이렇게 넘어가도록 하자.

'DB > JPA' 카테고리의 다른 글

Join과 fetch join, 알고 쓰자.  (0) 2023.03.31
QueryDsl Custom Repository 명명 규칙  (0) 2023.03.31

+ Recent posts