DB에 대해 잘 모르는 상태로 ai의 도움을 받아 공부해서 작성한 게시글입니다.
M1 맥 환경에서 진행된 내용이라 윈도우 환경에서는 다른 부분이 있을것에 주의!
지금 하고있는 c++ 서버 프로젝트에서 DB를 쓰고싶어서 gemini의 도움을 받으면서 테스트 해봤다. 그런데 스무스하게 되지 않고 이런저런 문제가 있었다. 진행과 해결과정을 어디다 적어놓으면 좋을 것 같아서 블로그에 글을 남긴다.
일단 지금까지 한 부분은 도커 데스크탑으로 postgres 컨테이너를 띄우고, 터미널로 DB에 접속해 실제 데이터를 만들고 확인해봤다. 그다음엔 c 코드를 사용해서 컨테이너에서 돌아가고있는 DB 서버에 connect하고, c 코드로 sql쿼리를 실행시켜보았다.
1. 도커 컨테이너 띄우기.
- 도커 데스크탑 설치 : 난 도커 테스크탑은 이전에 설치해놨기에 그냥 그걸 썼다.
- postgres 이미지 다운로드 : 도커 데스크탑에서 16버전으로 다운로드 받았다.

- 컨테이너 띄우기 : 도커 데스크탑의 images에서 아까 받은 이미지의 삼각형을 누르면 된다.


옵션을 세팅하라고 뜨는데, 아래와 같이 설정했다. (다른 블로그 글을 참조했다)
name: postgres_test
port: 5432
Volume: pgdata | /var/lib/postgresql/data
Env: POSTGRES_PASSWORD | password
도커볼륨을 내pc에서 확인하고 싶다면 pgdata가 아닌 내 pc의 실제 경로를 써 줘야한다. (리눅스가 아닌 경우. 윈도우는 확인 안해봄)
ex) /Users/name/Desktop/docker_volume
2. postgres에 접속하기
터미널에서 docker exec -it postgres_test psql -U postgres 를 입력해 접속했다.
비밀번호 없이 들어갈 수 있는 이유는 postgres컨테이너 안의 pg_hba.conf 파일에서 아래와 같은 부분이 있기 때문이라 한다.
local all all trust <-- 여기서 통과!
host all all 127.0.0.1/32 trust <-- 여기서도 통과!
확실하게는 모르겠지만 아마 내가 접속하는 ip(localhost)가 신뢰할 수 있는 ip로 등록되어있기 때문으로 보인다.
아니면 컨테이너 터미널 환경변수에 POSTGRES_PASSWORD=password 이 등록되어있어서 그런걸지도?
너무 깊게 파진 말자. 지금은
결론부터 말하면 도커는 맥에서 돌아가는게 아니라 맥/리눅스 가상머신/도커 이렇게 쌓여서 돌아가기 때문.
처음에 컨테이너를 띄울 때 도커볼륨을 host의 pgdata와 컨테이너의 /var/lib/postgresql/data를 연결해놨다.(볼륨 마운트). 여기서 pgdata는 도커 볼륨의 이름이고 실제 위치는 docker volume inspect pgdata 명령어로 확인할 수 있는데 "Mountpoint": "/var/lib/docker/volumes/pgdata/_data" 라고 나온다. 하지만 host pc(Mac)에서 해당위치를 가려 해봐도 아무것도 없다. 왜냐하면 맥 위에 올려진 리눅스 가상머신의 경로이기 때문.
도커는 본래 리눅스용으로 만들어진것이기 때문에 맥이나 window에서 도커를 쓰면 먼저 리눅스 가상머신을 올리고, 그 위에서 도커를 실행하고, 또 그 위에서 컨테이너를 띄우는 방식으로 돌아간다고 한다. 그래서 실제로 docker의 호스트는 mac이 아니라 리눅스 가상머신인 것.
만약 pgdata 대신 /Users/name/Desktop/docker_volume 와 같이 구체적인 경로를 적어넣는다면 볼륨마운트가 아닌 바인드 마운트 방식으로 VM이 맥의 해당 폴더를 네트워크 공유 폴더처럼 빌려와서 보여준다고 한다. 이렇게 하면 맥에서 접근할 수 있다. (확인해봄)
3. 테이블 생성, 데이터 넣고 확인하기
CLI 로 접속한 상태 에서 아래와 같은 쿼리들을 실행해줬다.
-- 사용자 테이블 생성
CREATE TABLE users (
id SERIAL PRIMARY KEY, -- 자동 증가하는 고유 번호
username VARCHAR(50) UNIQUE NOT NULL, -- 중복 안되는 이름
email TEXT NOT NULL, -- 이메일
created_at TIMESTAMP DEFAULT NOW() -- 생성 일시 (기본값 현재시간)
);
-- 게시글 테이블 생성 (관계 설정)
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title VARCHAR(200) NOT NULL,
content TEXT,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, -- 외래키: 유저와 연결
created_at TIMESTAMP DEFAULT NOW()
);
-- 참고: ON DELETE CASCADE는 유저가 삭제되면 그 유저가 쓴 글도 자동으로 지워지게 하는 옵션입니다. 깔끔하죠?
-- 데이터 삽입
INSERT INTO users (username, email) VALUES ('gemini', 'gemini@example.com');
INSERT INTO posts (title, content, user_id) VALUES ('첫 번째 글', '반가워요!', 1);
-- 데이터 조회 (Join 사용)
SELECT u.username, p.title, p.content
FROM users u
JOIN posts p ON u.id = p.user_id;

4. c 코드로 쿼리 실행하기
- libpq-fe.h 헤더를 찾지 못하는 문제
libpq 라이브러리가 설치되지 않았기 때문에 발생하는 문제. 설치해주자.
brew install libpq
- libpq-fe.h 헤더를 찾지 못하는 문제 (2)
라이브러리는 설치됐지만 PATH에 등록되어있지 않아서 생기는 문제.
터미널에서 아래 명령어를 입력해주면 된다.
export LIBRARY_PATH="/opt/homebrew/opt/libpq/lib:$LIBRARY_PATH"
export CPATH="/opt/homebrew/opt/libpq/include:$CPATH"
각 경로는 brew --prefix libpq 를 적으면 나오는 경로 뒤에 (brew로 받은 패키지의 위치를 찾는 명령어) /lib 와 /include를 추가한 것이다. 아마 brew로 받는 라이브러리라면 이런 방법으로 include 문제와 link문제를 해결할 수 있을 듯 하다.
단 이렇게 하면 해당 터미널을 닫으면 다 날라간다.
그러니 아래와 같이 명령어를 입력하자.
echo export LIBRARY_PATH="/opt/homebrew/opt/libpq/lib:$LIBRARY_PATH" >> ~/.zshrc
echo export CPATH="/opt/homebrew/opt/libpq/include:$CPATH" >> ~/.zshrc

.zshrc 는 z shell을 킬때마다 실행시켜주는 쉘 스크립트다. 즉 내 mac의 zsh에서는 항상 해당 환경변수가 존재하는 것.
당연히 쉘이 다르면 파일이름도 다를 것이고, 윈도우에서는 아예 다른 방법을 사용해야 할 수도 있다. (아마 설정 - 환경변수에 가서 직접 추가해야 할 것)
- 링킹 에러 발생

컴파일은 되는데 링킹 과정에서 문제가 생긴다. 헤더는 잘 갖고왔는데 라이브러리를 못가져온것.
헤더는 잘 읽어서 컴파일로 o파일을 만드는데까지는 성공했으나 o파일로 바이너리 파일을 만들 때 실패한 것.
컴파일러 빌드 옵션에 -lpq 를 추가해준다. 나는 Makefile로 작업하고 있어서 Makefile에 추가해줬다.
- libpq-fe.h 헤더를 찾지 못하는 문제 (3)
사실 이제 컴파일도 되고 빌드도 되지만 vscode상에서 헤더 부분에 빨간줄이 지워지지 않았다.

.vscode 폴더안에 c_cpp_properties.json에서 해당 경로를 추가해주면 해결된다.
드디어 모든 오류가 해결돼서 아래의 코드로 test파일을 빌드할 수 있었다.
#include <iostream>
#include <libpq-fe.h>
void exit_nicely(PGconn *conn) {
PQfinish(conn);
exit(1);
}
int main() {
// 1. DB 연결 설정 (사용자 환경에 맞게 수정하세요)
// 컨테이너 내부라면 host는 보통 localhost입니다.
const char *conninfo = "dbname=postgres user=postgres password=password host=localhost";
PGconn *conn = PQconnectdb(conninfo);
if (PQstatus(conn) != CONNECTION_OK) {
std::cerr << "연결 실패: " << PQerrorMessage(conn) << std::endl;
exit_nicely(conn);
}
std::cout << "PostgreSQL에 성공적으로 연결되었습니다!" << std::endl;
// 2. SQL 쿼리 실행 (users 테이블 조회)
PGresult *res = PQexec(conn, "SELECT id, username, email, created_at FROM users");
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
std::cerr << "조회 실패: " << PQerrorMessage(conn) << std::endl;
PQclear(res);
exit_nicely(conn);
}
// 3. 결과 출력
int nFields = PQnfields(res);
int nTuples = PQntuples(res);
std::cout << "\n--- [Users 테이블 조회 결과] ---" << std::endl;
// 헤더(컬럼명) 출력
for (int i = 0; i < nFields; i++) {
printf("%-15s", PQfname(res, i));
}
printf("\n");
printf("------------------------------------------------------------\n");
// 데이터 행(Row) 출력
for (int i = 0; i < nTuples; i++) {
for (int j = 0; j < nFields; j++) {
printf("%-15s", PQgetvalue(res, i, j));
}
printf("\n");
}
// 4. 정리
PQclear(res);
PQfinish(conn);
return 0;
}

c 코드로 도커의 postgres DB 서버에 연결해 쿼리를 실행해보는데 성공했다!
'공부 > DB' 카테고리의 다른 글
| postgreSQL INDEX (0) | 2026.02.15 |
|---|---|
| postgreSQL JOIN (0) | 2026.02.15 |
| 관계형DB 정규화와 정규형 (0) | 2026.02.14 |
| DBeaver 사용하기 (0) | 2026.02.13 |
| SQL CRUD (0) | 2025.09.04 |