[Next.js] 1장 + 자동/수동 설치 Q&A 복습 Next.js Learn 1장(대시보드 설치)과 수동 설치를 직접 해보며 생긴 질문 정리. 한눈에 보기 개념 한 줄 정의 패키지(모듈) 남이 만들어 공개한 코드 부품 (react, next 등) npm 부품을 설치·관리하는 도구. Node.js와 함께 설치됨 pnpm npm과 같은 역할. 더 빠르고 저장 공간 절약 npx 도구를 설치하지 않고 한 번만 빌려서 실행 package.json 프로젝트 정보 + 필요한 부품 목록 + 실행 명령어 node_modules 실제로 받아온 부품 코드가 쌓이는 창고 1. npm / pnpm / npx Q. npm이 뭔가? A. 부품 가게 + 배달원. 필요한 패키지를 받아 프로젝트에 넣어줌. npm create vite@latest → 프로젝트 틀 만들기 npm install → 필요한 부품 전부 설치 npm run dev → 미리보기 서버 켜기 Q. pnpm은 npm과 뭐가 다른가? A. 같은 부품을 가져오지만 더 빠름 여러 프로젝트가 같은 부품을 쓰면 한 번만 저장 설치 스크립트를 허락받고 실행 (npm은 자동 실행) npm pnpm npm install pnpm i npm install 이름 pnpm add 이름 npm run dev pnpm dev npx 도구 pnpm dlx 도구 Q. pnpm을 깔았는데 왜 npx로 불러오나? A. 역할이 다름. npx = create-next-app 같은 일회용 도구를 빌려 실행 pnpm = 프로젝트에 부품 설치 --use-pnpm 옵션으로 도구가 부품 설치를 pnpm에 맡김 Q. npm install -g pnpm 에서 -g 는? A. global. 특정 프로젝트가 아니라 컴퓨터 전체 에 설치. 2. package.json / node_modules Q. package.json은 뭔가? A. 프로젝트 소개서 + 장보기 목록. 지금 중요한 건 두 부분. "scripts": { "dev": "next dev" }, "dependencies": { "next": "^16.3.6", "react": "^19.3.0", "react-dom": "^19.3.0" } scripts = 단축번호. pnpm dev → next dev 실행 dependencies = 필요한 부품 목록 (이름: 버전) ^16.3.6 = 16.x 중 이 버전 이상이면 OK keywords , author , license = 부품으로 공개할 때 쓰는 정보. 지금은 무시 Q. node_modules는 뭔가? A. package.json이 목록 이라면 node_modules는 실제로 사 온 재료 창고 . import Link from 'next/link' → node_modules의 next에서 꺼내 씀 직접 수정 X, 지워도 pnpm i 로 복구, 깃허브에 안 올림 Q. pnpm i 의 i는? A. install의 줄임말. Q. 목록에 적으면 다운받아 주는 건가? A. 맞음. 하지만 보통 손으로 안 적음. 명령어 언제 하는 일 pnpm i 프로젝트 처음 받았을 때 목록 전부 설치 pnpm add 이름 부품 새로 추가할 때 그것만 설치 + 목록에 자동 기록 3. 자동 설치 vs 수동 설치 Q. 자동 설치 명령어는 뭘 설치하는 건가? npx create-next-app@latest nextjs-dashboard --example "깃허브주소" --use-pnpm A. Next.js가 들어 있는 새 프로젝트 폴더 를 만듦. nextjs-dashboard = 폴더 이름 --example = 강의용 코드로 시작 Next.js는 컴퓨터가 아니라 프로젝트 폴더 안 에만 설치됨 Q. 강의용 코드는 강의 사이트에 있나? A. 설명은 nextjs.org/learn(교재), 코드는 github.com/vercel/next-learn(실습 파일). Q. 부품이 왜 이미 다 깔려 있나? A. 예제에 딸려 온 package.json 목록을 보고 pnpm이 자동 설치. 내가 부품 이름을 하나도 안 적었으므로 자동 설치 . Q. 수동 설치는 필요한 모듈만 고르는 건가? A. 반은 맞음. 빈 폴더에서 처음부터 직접 조립 하는 것. 단계 수동 설치에서 직접 한 일 자동 설치 1 pnpm init (빈 목록) 알아서 2 pnpm add next react react-dom 알아서 3 package.json에 "dev": "next dev" 알아서 4 app/layout.js 작성 알아서 5 app/page.js 작성 알아서 react-dom = 리액트를 브라우저 화면(DOM)에 그려주는 부품 Q. scripts의 test 줄은 왜 바꾸나? A. pnpm init 이 넣은 예시 줄(에러 문구만 출력). dev 를 등록해야 pnpm dev 가 작동. Q. name은 왜 바꾸나? A. 이름 규칙: 영어 소문자, 숫자, - 만. 띄어쓰기·한글은 에러 가능. 4. app 폴더와 layout / page Q. app 폴더는 앱을 만드는 건가? A. 아니. 웹사이트(애플리케이션)의 페이지가 들어가는 특별 폴더 . 폴더 구조 = 주소 (App Router). 파일 주소 app/page.js / app/about/page.js /about Q. app 안에 파일을 왜 만들어야 하나? A. Next.js 설치 = 빈 극장. 올릴 내용이 없음. Next.js는 정해진 자리에서 파일을 찾음. 접속 → app/page.js 찾기(내용) → app/layout.js로 감싸기 → 화면 page.js 없음 → 404 layout.js 없음/비어 있음 → 에러 폴더 이름이 app이 아님 → 못 찾음 Q. layout.js는 왜 필요한가? A. 모든 페이지를 감싸는 액자. <html> , <body> 뼈대 담당 (Vite의 index.html 역할). export default function RootLayout({ children }) { return ( <html lang="ko"> <body>{children}</body> </html> ) } Q. lang="ko" 는 한국어 설정인가? A. 번역 설정 X. "이 페이지는 한국어"라는 이름표 (화면 낭독기, 번역 제안, 검색엔진용). Q. children은 임의의 이름인가? A. React가 정한 이름. 태그 사이에 넣은 자식 요소가 자동으로 담기는 props. title , items = 내가 지은 이름 (변경 가능) children = 고정 (변경 불가) { children } = props.children 의 줄임 Q. 자식 노드를 받아주는 함수 같은 건가? A. 받는 건 컴포넌트 함수, children 은 받은 자식 노드 자체 . Q. 폴더 이름은 다 규칙인가? 종류 예 이름 변경 Next.js 규칙 app/ , public/ , page , layout 불가 정리용 관례 lib/ , ui/ , definitions.ts 가능 5. 대시보드 폴더 구조 (1장) 폴더/파일 기능 app/ 페이지, 컴포넌트, 로직. 메인 작업 공간 app/lib/ 재사용 함수, 데이터 가져오기 app/lib/placeholder-data.ts DB 대신 쓰는 연습용 가짜 데이터 app/lib/definitions.ts 데이터 모양 규칙(타입) app/ui/ 카드, 표, 폼 등 UI 컴포넌트 public/ 이미지 등 정적 파일 next.config.ts Next.js 설정 (강의에선 수정 X) .ts , .tsx = TypeScript (JS + 타입 규칙) 1장 결과 화면은 스타일 없는 Acme 페이지 + 거대한 화살표 (정상, 2장에서 스타일링) 6. 만난 에러와 해결 에러 뜻 해결 EACCES: permission denied 시스템 폴더에 설치 권한 없음 npm 설치 위치를 내 사용자 폴더로 변경 ( npm config set prefix ~/.npm-global ) sudo: 3 incorrect password attempts 관리자 비밀번호 실패 위 방법으로 sudo 없이 해결 Could not resolve host 사이트 주소 연결 실패 네트워크 문제, 다른 방법 사용 command not found: --example 명령어 뒷부분만 입력됨 한 줄 전체 복사해서 실행 ERR_PNPM_IGNORED_BUILDS 설치 스크립트 실행 허락 필요 (bcrypt, sharp) pnpm approve-builds → a → y → pnpm i Port 3000 is in use 다른 서버가 3000번 사용 중 자동으로 3001 사용, 그 주소로 접속 default export is not a React Component in "/layout" layout.js가 비었거나 저장 안 됨 코드 붙여넣고 Cmd+S 비밀번호 입력 시 화면에 아무것도 안 보이는 건 정상 터미널 끝 폴더명 % 로 현재 위치 확인, cd .. 은 상위 폴더로
1. 왜 JSONB가 필요할까? 문서의 메타데이터를 저장한다고 생각해 보자. 메타데이터 는 문서의 제목, 작성자, 회사, 문서 유형처럼 문서를 설명하는 정보다. 문서마다 가지고 있는 정보는 다를 수 있다. 문서 포함된 정보 사업보고서 작성자, 회사, 문서 유형 감사보고서 작성자, 회사, 회계연도, 감사인 반기보고서 작성자, 회사, 태그, 페이지 수 모든 정보를 일반 컬럼으로 만들면 새로운 문서 유형이 들어올 때마다 컬럼을 추가해야 할 수 있다. 이런 변경이 반복되면 다음 문제가 생긴다. 특정 문서에서만 사용하는 컬럼이 많아진다. 해당 정보가 없는 행에는 NULL이 많이 생긴다. 새로운 필드가 생길 때마다 테이블 구조를 변경해야 한다. 운영 중 스키마 변경에 따른 잠금과 관리 부담이 발생할 수 있다. 핵심 문제는 컬럼 수 자체보다, 앞으로 어떤 필드가 생길지 미리 알기 어렵다는 점이다. JSONB는 행마다 필드 구성이 다른 데이터를 PostgreSQL에 저장할 때 유용하다. 모든 데이터를 JSONB에 넣기보다, 공통 핵심 정보는 일반 컬럼에 두고 가변적인 부가 정보는 JSONB에 저장한다. 2. 정형 데이터와 반정형 데이터 구분 특징 예시 정형 데이터 정해진 컬럼과 자료형을 따름 관계형 테이블 반정형 데이터 구조는 있지만 포함되는 필드가 달라질 수 있음 JSON, XML, YAML 비정형 데이터 고정된 행·열이나 키 구조로 표현되지 않음 자유 텍스트, 이미지 JSON은 키와 값의 쌍으로 정보를 표현하는 형식 이다. { "author": "김영수", "company": "삼성", "tags": ["공시", "연간"] } 요소 의미 "author" 키: 정보의 이름 "김영수" 값: 실제 내용 { ... } 객체: 키와 값을 묶은 구조 [ ... ] 배열: 여러 값을 담은 구조 다른 문서에는 tags 가 없고 financial_year 가 있을 수도 있다. 테이블의 컬럼은 그대로 유지하면서, JSONB 컬럼 내부의 키 구성을 행마다 다르게 저장하는 것이다. 유연함에는 대가가 있다 JSONB를 사용하면 필드를 유연하게 저장할 수 있지만, 일반 컬럼처럼 내부 필드마다 자료형을 지정한 것은 아니다. 예를 들어 페이지 수를 숫자로 저장할 수도 있고, 문자열로 저장할 수도 있다. {"pages": 220} {"pages": "220페이지"} 이런 차이는 나중에 숫자 비교나 집계에서 문제가 될 수 있다. 또한 JSONB 내부 필드의 통계를 이용한 조회 건수 예측은 일반 컬럼보다 어려울 수 있다. JSONB는 정형 테이블을 없애는 방법이 아니라, 고정하기 어려운 부가 정보를 함께 담는 방법이다. 3. json과 jsonb의 차이 PostgreSQL에는 json 과 jsonb 라는 두 가지 JSON 저장 타입이 있다. 3-1. json: 입력한 텍스트 보존 입력한 JSON 텍스트의 표현을 유지한다. 내부 값을 처리할 때는 텍스트를 해석하는 과정이 필요하다. 3-2. jsonb: 해석한 구조를 저장 저장할 때 JSON을 해석해 내부 이진 구조로 바꾼다. 이후 조회에서 그 구조를 활용하고, GIN 인덱스도 만들 수 있다. 여기서 파싱(parsing) 은 텍스트를 읽어 키·값·배열 등의 구조를 파악하는 과정이다. 항목 json jsonb 저장 형태 JSON 텍스트 원문 파싱된 내부 이진 구조 입력한 키 순서 보존 보존하지 않음 중복 키 원문에 유지 마지막 값만 유지 저장 시 처리 원문 저장 중심 구조 변환 비용 발생 내부 값 조회 읽을 때 파싱 필요 저장 시 파싱한 구조 활용 JSONB용 GIN 인덱스 직접 적용 불가 적용 가능 원문 보존이 중요하면 JSON, 내부 데이터를 검색하고 활용하려면 JSONB를 고려한다. 3-3. 중복 키 예시 입력 데이터가 다음과 같다고 하자. { "b": 2, "a": 1, "a": 99 } json 은 입력한 표현을 유지하지만, jsonb 에서는 a 의 마지막 값인 99가 남는다. {"a": 99, "b": 2} 따라서 JSONB를 사용할 때는 입력한 키 순서나 중복 키 보존에 의존하지 않는다. 4. JSONB 컬럼에 문서 정보 저장하기 4-1. 테이블 생성 CREATE TABLE documents ( id SERIAL PRIMARY KEY, title TEXT, metadata JSONB ); 컬럼 역할 id 문서를 구별하는 번호 title 문서 제목 metadata 문서마다 다른 부가 정보 4-2. 데이터 입력 INSERT INTO documents (title, metadata) VALUES ('삼성 사업보고서', '{"author":"김영수","company":"삼성","doc_type":"사업보고서","tags":["공시","연간"]}'), ('LG 감사보고서', '{"author":"이미영","company":"LG","doc_type":"감사보고서","financial_year":2025}'), ('현대 반기보고서', '{"author":"박지수","company":"현대","doc_type":"반기보고서","tags":["공시","반기"],"pages":220}'); 문서 모두 metadata 에 저장하지만 내부 키 구성은 다르다. 삼성 문서에는 tags 가 있다. LG 문서에는 financial_year 가 있다. 이 차이 때문에 테이블 컬럼을 추가할 필요는 없다. SQL에서는 바깥쪽 작은따옴표로 JSON 내용을 감싸고, JSON 안의 키와 문자열에는 큰따옴표를 사용한다. 5. JSONB 연산자 JSONB 연산자는 목적에 따라 구분하면 이해하기 쉽다. 목적 연산자 결과 값 꺼내기 -> JSONB 값 꺼내기 ->> TEXT 중첩 경로로 꺼내기 #> JSONB 중첩 경로로 꺼내기 #>> TEXT 지정한 JSON 포함 확인 @> 참·거짓 키 하나 존재 확인 ? 참·거짓 여러 키 중 하나라도 존재 ?| 참·거짓 지정한 키 모두 존재 ?& 참·거짓 5-1. ->: JSONB 형태로 꺼내기 SELECT metadata -> 'company' FROM documents; metadata 에서 company 값을 꺼내되 JSONB 형태를 유지한다. 문자열 값이라면 다음처럼 표현된다. "삼성" ← 큰따옴표 포함 배열의 원소도 꺼낼 수 있다. SELECT metadata -> 'tags' -> 0 FROM documents WHERE id = 1; 다음 순서로 해석한다. metadata 에서 tags 배열을 꺼낸다. 배열의 0번 원소를 꺼낸다. JSON 배열의 첫 원소는 0번이므로 결과는 "공시" 다. 5-2. ->>: TEXT로 꺼내기 SELECT metadata ->> 'company' FROM documents; 같은 값을 꺼내지만 결과 타입은 TEXT다. 삼성 문자열 조건을 비교할 때 사용할 수 있다. SELECT * FROM documents WHERE metadata ->> 'doc_type' = '감사보고서'; ->와 ->>의 차이는 출력 모양만이 아니라 반환 타입의 차이다. 숫자 비교 시 주의 metadata ->> 'pages' JSON 안에서 pages 가 숫자였더라도 ->> 로 꺼낸 결과는 TEXT다. 숫자로 비교하려면 형변환한다. SELECT * FROM documents WHERE (metadata ->> 'pages')::INT > 100; metadata ->> 'pages' : 페이지 수를 텍스트로 추출 ::INT : 정수로 변환 > 100 : 100보다 큰지 비교 단, "220페이지" 처럼 정수로 바꿀 수 없는 값이 들어 있다면 변환에 실패할 수 있으므로 데이터 형식을 일관되게 관리해야 한다. 5-3. #>와 #>>: 중첩 경로 접근 다음처럼 객체 안에 객체가 들어 있을 수 있다. {"info":{"author":{"name":"홍길동","dept":"재무"}}} info → author → name 경로의 값을 JSONB로 꺼내려면: SELECT metadata #> '{info,author,name}' FROM documents; 결과는 "홍길동" 이다. 부서 값을 TEXT로 꺼내려면: SELECT metadata #>> '{info,author,dept}' FROM documents; 결과는 재무 다. 중첩된 키를 따라가는 경로를 {...} 안에 순서대로 적는다. 5-4. @>: 포함 여부 -- "지정한 JSON이 포함되어 있는가" — GIN 인덱스를 탄다 ★ SELECT * FROM documents WHERE metadata @> '{"company":"삼성"}'; -- 삼성 관련 문서만 반환 “metadata에 company가 삼성이라는 내용이 포함된 문서”를 찾는다. metadata 전체가 오른쪽 JSON과 완전히 같아야 하는 것은 아니다. 다른 키가 함께 있어도 조건을 만족할 수 있다. 태그 배열에서 특정 태그 포함 여부도 확인할 수 있다. SELECT * FROM documents WHERE metadata @> '{"tags":["반기"]}'; tags 배열에 "반기" 가 포함된 문서를 찾는다. @>는 GIN 인덱스를 활용할 수 있는 주요 연산자다. 5-5. ?: 키 존재 여부 SELECT * FROM documents WHERE metadata ? 'financial_year'; 회계연도가 어떤 값인지를 비교하는 것이 아니라, financial_year 라는 키가 있는지 확인한다. 여러 키 중 하나라도 있으면: WHERE metadata ?| ARRAY['financial_year', 'pages'] 두 키가 모두 있어야 하면: WHERE metadata ?& ARRAY['author', 'company'] 6. 왜 JSONB 검색에는 GIN이 필요할까? 6-1. B-tree와 JSONB 내부 검색 B-tree는 일반 컬럼의 값이나 특정 표현식의 결과를 정렬해 관리한다. 회사명이 일반 TEXT 컬럼이라면 company = '삼성' 같은 검색에 사용할 수 있다. 하지만 JSONB 한 값 안에는 여러 정보가 함께 들어 있다. { "author": "박지수", "company": "현대", "tags": ["공시", "반기"] } JSONB 전체에 B-tree를 만든다고 해서, 그 안의 각 키·값과 배열 원소가 따로 검색 가능해지는 것은 아니다. JSONB 전체 값의 비교와, 내부에 특정 정보가 포함되는지 찾는 작업은 다르다. 6-2. GIN은 역색인 GIN은 검색할 항목에서 그 항목을 포함하는 행 목록을 찾는 인덱스 다. 일반적인 데이터 읽기 방향은 다음과 같다. 행 포함한 값 1 a, b, c 2 b, d 3 a, c, e 역색인은 방향을 바꾼다. 검색할 값 포함한 행 a 1, 3 b 1, 2 c 1, 3 d 2 e 3 a 를 찾으려고 모든 행을 읽는 대신, 인덱스에서 a 를 찾아 관련 행을 좁힌다. 책 전체를 읽어 단어를 찾는 대신, 책 뒤의 찾아보기에서 해당 단어가 있는 페이지를 확인하는 방식이다. GIN 인덱스 생성 -- 기본 GIN 인덱스: 키, 값, 키+값 쌍을 모두 인덱싱 CREATE INDEX idx_meta_gin # 인덱스 생성 맻 이름 지정 ON documents # 대상 테이블 USING GIN (metadata); # GIN 방식 선택 (인텍싱할 컬럼) 이후 다음 조건에서 인덱스를 활용할 수 있다. WHERE metadata @> '{"company":"삼성"}' 다만 사용할 수 있다는 것과 실제로 선택된다는 것은 다르다. 테이블이 작거나 전체를 읽는 비용이 낮다면 옵티마이저가 Seq Scan을 선택할 수도 있다. 7. GIN이 있어도 ->> 비교는 왜 다를까? 다음 두 조건은 회사가 삼성인 문서를 찾는다는 점에서 비슷하다. WHERE metadata @> '{"company":"삼성"}' WHERE metadata ->> 'company' = '삼성' 하지만 수행하는 연산이 다르다. 조건 처리 방식 대응 인덱스 @> JSON 포함 여부 검사 JSONB GIN ->> ... = ... TEXT를 추출한 뒤 값 비교 해당 추출식의 표현식 인덱스 metadata 전체에 만든 GIN은 ->> 로 꺼낸 텍스트의 등치 비교를 직접 지원하지 않는다. 표현식 인덱스로 해결 CREATE INDEX idx_meta_company ON doc_meta ((metadata ->> 'company')); 표현식 인덱스 는 컬럼 원본이 아니라 계산한 결과에 만드는 인덱스다. 여기서는 다음 계산 결과를 인덱싱한다. metadata ->> 'company' 따라서 아래 조회에 활용할 수 있다. SELECT * FROM doc_meta WHERE metadata ->> 'company' = '삼성'; USING 을 별도로 지정하지 않은 이 인덱스는 기본 B-tree 방식이다. 회사명 추출식에 만든 인덱스이므로, 다른 필드를 검색하는 모든 조건까지 해결해 주는 것은 아니다. 필드별 인덱스가 늘어나면 저장 공간과 관리 비용도 증가한다. 8. 기본 GIN과 jsonb_path_ops GIN에도 어떤 연산을 지원하고 어떻게 인덱싱할지 정하는 연산자 클래스(operator class) 가 있다. 항목 기본 GIN ( jsonb_ops ) jsonb_path_ops 생성 방법 USING GIN (metadata) USING GIN (metadata jsonb_path_ops) 지원 연산자 @> , ? , ?| , ?& @> 만 인덱스 크기 크다 (키·값 쌍 모두) 작다 (경로 해시만) @> 검색 속도 빠름 더 빠름 키 존재 검색 ( ? ) 가능 ❌ 불가 -- 기본 GIN (키 존재 검색도 필요할 때) CREATE INDEX idx_meta_default ON documents USING GIN (metadata); -- jsonb_path_ops (오직 @> 검색만 하고 인덱스를 작게 유지하고 싶을 때) CREATE INDEX idx_meta_path ON documents USING GIN (metadata jsonb_path_ops); 키가 있는지 확인하는 검색까지 필요하다면 기본 GIN을 고려하고, 포함 검색이 중심이라면 jsonb_path_ops를 비교한다. 인덱스 크기 확인 SELECT pg_size_pretty(pg_relation_size('idx_meta_default')) AS default_gin_size, pg_size_pretty(pg_relation_size('idx_meta_path')) AS path_ops_size; -- 결과 예시: default_gin_size 112 kB, path_ops_size 80 kB 함수 역할 pg_relation_size() 저장 크기 확인 pg_size_pretty() 사람이 읽기 쉬운 단위로 표시 9. EXPLAIN으로 인덱스 사용 확인하기 9-1. 어떤 연산자가 GIN을 타는가 -- 인덱스 없이: @> 도 Seq Scan EXPLAIN SELECT * FROM doc_meta WHERE metadata @> '{"company":"삼성"}'; -- GIN(기본, jsonb_ops) 생성 후 CREATE INDEX idx_meta_default ON doc_meta USING GIN (metadata); -- @> 는 인덱스를 탄다 EXPLAIN SELECT * FROM doc_meta WHERE metadata @> '{"company":"삼성"}'; -- 그런데 ->> 비교는 여전히 Seq Scan (GIN이 못 도와줌) EXPLAIN SELECT * FROM doc_meta WHERE metadata ->> 'company' = '삼성'; 9-2. 표현식 인덱스로 ->> 해결하기 -- 표현식 인덱스로 ->> 조건도 인덱스 타게 만들기 CREATE INDEX idx_meta_company ON doc_meta ((metadata ->> 'company')); EXPLAIN SELECT * FROM doc_meta WHERE metadata ->> 'company' = '삼성'; -- → Bitmap Heap Scan + Bitmap Index Scan on idx_meta_company (더 이상 Seq Scan 아님) 실행 계획에서 볼 항목 표시 의미 Seq Scan 테이블을 순차적으로 읽음 Bitmap Index Scan on 인덱스명 해당 인덱스로 후보 위치를 찾음 Bitmap Heap Scan 후보 위치를 바탕으로 실제 테이블 데이터를 읽음 EXPLAIN : 실행 계획 확인 EXPLAIN ANALYZE : 실제로 실행한 결과까지 확인 어떤 연산자를 사용했는지와, 그 연산에 맞는 인덱스가 있는지를 함께 확인한다. 10. 인덱스는 읽기를 빠르게 하지만 비용도 든다 JSONB 한 행에는 여러 키와 값이 포함될 수 있다. GIN은 이 정보를 검색할 수 있도록 여러 인덱스 항목을 관리한다. 따라서 INSERT·UPDATE 시 데이터뿐 아니라 인덱스도 함께 관리해야 한다. 얻는 것 드는 비용 필요한 행을 빠르게 찾을 수 있음 인덱스 저장 공간 전체 테이블을 읽는 작업 감소 가능 삽입·수정 시 갱신 작업 포함·키 존재 검색 지원 인덱스 유지·관리 부담 갱신 비용을 완화하기 위해 pending list 를 쓴다. 변경 내용을 모아 두었다가 나중에 인덱스에 병합하는 방식으로, 비용을 완전히 없애는 것은 아니다. 모든 필드에 인덱스를 만들기보다, 실제로 자주 사용하는 검색 조건에 맞춰 선택해야 한다. 11. 하이브리드 설계: 자주 쓰는 필드는 컬럼으로 처음에는 어떤 정보가 들어올지 몰라 JSONB에 저장했더라도, 시간이 지나면 자주 사용하는 필드가 정해질 수 있다. 예를 들어 대부분의 조회와 집계가 다음을 기준으로 이루어진다고 하자. 회사: company 문서 유형: doc_type 이 경우 해당 값을 일반 컬럼으로 옮겨 관리하는 방식을 고려할 수 있다. 저장 위치 적합한 정보 일반 컬럼 자주 검색·집계·JOIN하는 핵심 정보 JSONB 문서마다 다르거나 변경이 잦은 부가 정보 이렇게 두 방식을 함께 사용하는 것이 하이브리드 설계 다. 컬럼 승격의 진행 순서 company , doc_type 일반 컬럼을 추가한다. 기존 JSONB에서 값을 꺼내 새 컬럼에 채운다. JSONB의 값과 새 컬럼의 값이 일치하는지 확인한다. 일반 컬럼에 B-tree 인덱스를 만든다. JSONB 검색과 일반 컬럼 검색의 실행 계획을 비교한다. 이후 입력되는 데이터도 새 컬럼을 채우도록 INSERT 방식을 변경한다. 기존 데이터를 새 컬럼에 채워 넣는 작업을 백필(backfill) 이라고 한다. 중복 저장도 관리해야 한다 같은 회사명을 일반 컬럼과 JSONB에 모두 남겨두면 같은 정보가 두 곳에 존재한다. 중요한 것은 JSONB를 계속 유지하는 것 자체가 아니라, 사용 패턴에 맞게 필드의 저장 위치를 결정하는 것이다. 12. Python에서 JSONB 데이터를 전달할 때 SQL 안에 JSON 문자열을 직접 작성하는 방식과, Python 딕셔너리를 매개변수로 전달하는 방식은 구분해야 한다. Python 딕셔너리를 psycopg에 그대로 전달하면 변환 방법을 찾지 못하는 오류가 발생할 수 있다. psycopg v3에서는 Jsonb 어댑터를 사용한다. from psycopg.types.json import Jsonb 전달할 딕셔너리를 다음처럼 감싼다. Jsonb(dict_value) 이것은 해당 Python 값을 PostgreSQL의 JSONB로 전달할 수 있도록 변환을 맡기는 것 이다. 13. 검색 목적에 따른 선택 기준 하고 싶은 작업 사용할 방법 고려할 인덱스 JSONB 형태로 값 꺼내기 -> , #> 추출 자체와 검색 조건을 구분 텍스트로 꺼내 값 비교 ->> , #>> 해당 표현식 인덱스 특정 JSON 내용 포함 확인 @> GIN 키 존재 여부 확인 ? , ?| , ?& 기본 GIN 같은 필드로 반복 집계·JOIN 일반 컬럼으로 승격 검토 B-tree JSONB는 유연하게 저장할 수 있게 해 주고, 인덱스는 필요한 내용을 효율적으로 찾게 해 준다. 두 기능을 연결하려면 검색 연산자와 인덱스의 대응 관계 를 이해해야 한다.
1. 관계 데이터 모델의 개념 1-1 릴레이션의 개념 - 행(row)과 열(column)로 구성된 2차원의 테이블(table) 속성 : 릴레이션의 열 튜플 : 릴레이션의 행 도메인 : 하나의 속성의 가질 수 있는 값들의 집합 1-2 릴레이션의 특징 속성의 원자성: 속성은 원자값(단일값)만을 가진다. 속성의 무순서성 : 속성 사이의 순서는 무의미하다. 속성의 동일성 : 정의된 도메인에 속하는 동일한 유형의 값만 가진다. 튜플의 유일성 : 중복된 튜플이 존재하지 않는다. 튜플의 무순서성 : 튜플 사이의 순서는 무의미하다. 2. 무결성 제약조건 2-1 키 - 릴레이션에서 특정 튜블들을 유일하게 구별하는 속성 또는 속성들의 집합. (유일성 & 최소성의 특징을 가진다.) 키의 종류 1 슈퍼키 : 유일성을 만족하는 속성 2 후보키 : 유일성과 최소성을 만족하는 속성 3 기본키 : 후보키 중에서 기본적으로 사용하기 위해 선택한 키 4 대체키 : 기본키로 선택되지 못한 후보키 5 대리키 : 인위적으로 만든 기본키. 편의성과 안전성을 위해 사용 6 * 외래키 * - 다른 릴레이션의 기본키를 참조하는 속성, NULL 가능 (릴레이션 간의 참조관계) <외래키 유형> 1 2 3 <요약> ◎ 릴레이션 키 기본키 : 유일성과 최소성을 만족하는 속성 또는 속성 집합 중에서 하나를 선택해 기준을 사용하는 키 외래키 : 다른 릴레이션의 기본키를 참조하는 속성 또는 속성 집합 2-2 무결성 제약조건 무결성: 데이터에 결함이 없는 상태, 즉 데이터가 정확하고 유효하게 유지된 상태 무결성 제약조건 ◎ 도메인 무결성 제약조건 - 도메인 제약 : 릴레이션 내의 튜플들이 각 속성의 도메인에 지정된 값만 가져야한다는 조건 ◎ * 개체 무결성 제약 조건 * - 기본키 제약 : 기본키는 NULL값을 가져서는 안되며 릴레이션 내에 오직 하나의 값만 존재해야함을 지켜야한다는 조건 ◎ 참조 무결성 제약 조건 - 외래키 제약 : 릴레이션 간의 참조 관계를 선언하는 제약조건 - 자식 릴레이션의 외래키는 부모 릴레이션의 기본키와 도메인이 같아야하며, 자식 릴레이션의 값이 변경될 때 부모 릴레이션의 제약을 받는다. 3. 관계대수 릴레이션을 처리하는 연산자들의 모임 - 일반 집합 연산자 - 순수 관계 연산자 ◆ 셀렉션 예 <고객 릴레이션에서 등급이 gold이고, 적립금이 2000 이상인 튜플 검색> ◆ 프로젝션 예 ◆ 조인 (두 릴레이션의 공통 속성을 이준으로 속성값이 같은 튜플을 수평으로 결합하는 연산) 세타조인 동등조인 자연조인 외부조인(왼쪽 외부 조인, 오른쪽 외부 조인, 완전 외부 조인)
18–21대 대선 분류표·미분류표 K 분석 요약 □ 분석 틀 분석 단위: 구·시·군 (18·19대 249곳, 20대 248곳, 21대 252곳) 분자: 네 선거 모두 보수 후보 (박근혜·홍준표·윤석열·김문수), 분모: 민주당 후보 R1: 분류표의 보수 후보 비율 = 보수 ÷ (보수 + 민주) R2: 미분류표의 보수 후보 비율 (같은 식) K = (미분류 보수/민주) ÷ (분류 보수/민주) 엑셀 시트 K 열과 같은 식이고, 1보다 크면 미분류표에 보수 후보 표가 상대적으로 많다는 뜻 □ 선거별 K 값 선거 K 구·시·군 평균 K 전국 합산 [95%] 비율의 비 전국 적합식 R2 = a + b·R1 R2 18대 (2012) 1.479 1.38 [1.33, 1.44] 1.154 0.042 + 1.062·R1 0.982 19대 (2017) 1.600 1.61 [1.56, 1.65] 1.315 0.053 + 1.112·R1 0.970 20대 (2022) 1.285 1.20 [1.14, 1.25] 1.090 −0.002 + 1.107·R1 0.985 21대 (2025) 1.315 1.23 [1.19, 1.27] 1.113 0.004 + 1.117·R1 0.982 네 선거 모두 K > 1 당선인과 무관: 보수 후보가 당선된 18·20대와 낙선한 19·21대 모두 같은 방향 구·시·군 평균이 전국 합산보다 큰 이유 평균은 작은 군과 큰 구를 똑같이 한 번씩 셈 R1이 서로 다른 지역을 하나로 합치면 K가 작아짐 □ 추세 ○ 수준 19대에 가장 높고, 20대에 가장 낮으며, 21대에 조금 오름 20대를 기준으로 R1 = 0.5에서 R2가 높은 정도: 18대 +0.022, 19대 +0.057, 21대 +0.011 (모두 p < 0.001) ○ 기울기 19·20·21대는 약 1.11로 같음 (20대와 차이 p 0.79, 0.40) 18대만 1.06으로 낮음 (p < 0.001) ○ 곡선 네 선거 모두 R12 항이 음수 (19대 −0.62로 가장 강함) 비율이 0–1 사이에 갇혀 생기는 모양 ○ 지역 패턴 K가 낮은 곳: 호남 (20대 장흥 0.80, 21대 진도 0.76) K가 높은 곳: 경북·경남 (20대 영덕 2.48, 21대 예천 1.90) 시도별 K의 상관: 19–20대 0.93, 20–21대 0.88로 해마다 비슷한 지역 순서가 되풀이됨 강남구·군위군은 네 선거 모두 적합선 아래에 있는 반복 이상점 □ 치우침의 원인 단서 ○ 19대 후보별 K (문재인 대비, 선관위 자료) 홍준표 1.61, 안철수 1.23, 유승민 0.92, 심상정 0.74, 기타 후보 2.56 같은 보수 후보인데 유승민은 K < 1 치우침은 이념보다 지지층의 표기 습관과 더 잘 맞음 고령층 지지 후보와 군소 후보가 미분류표에서 늘어남 ○ 무효표 무효표는 모두 미분류표로 분류됨 (19대 130,598장, 미분류의 9.6%) ○ R1과의 관계 log K와 R1의 상관: 18·19대 약 0.44, 20·21대 약 0.7 보수 후보 비율이 높은 지역일수록 K가 큼 ○ 21대 투표 유형별 K (전국 합산) 관내사전 1.36, 선거일 1.28, 관외사전 1.14 □ K 분포와 정규성 18대: K는 5% 수준에서 기각, log K는 모든 검정에서 채택 → 로그정규분포 19대: 종 모양이지만 정규·로그정규 모두 5% 수준에서 기각 20대: 양 끝에 튀는 값(첨도 2.89)이 있어 기각 21대: 경계 (Shapiro-Wilk p 0.053, D'Agostino p 0.18, Anderson-Darling만 기각) 공통: K의 흩어짐 가운데 87–96%는 표본오차가 아니라 실제 지역 차이 모든 지역에 한 가지 원인이 같은 크기로 작용한 모습이 아님 □ 자료 점검 결과 ○ 18대 data18과 뉴스타파 원자료: 158곳 완전 일치, 40곳은 소수 표 차이, 51곳은 수개표를 포함 공개값보다 적은 부분은 모두 수개표 (투표구 누락 없음) K는 거의 같음 (구·시·군 평균 1.479, 뉴스타파 1.481) ○ 19대 data19와 선관위 자료가 249곳 모두 일치 봉화군은 선관위 소계 행에서 2,159표가 빠져 있어 개표단위 합계로 고침 (K 1.533 → 1.535) ○ 20대 오산은 분류표를 복원하고, 제천은 복원할 근거가 없어 뺌 ○ 21대 개표상황표 판독 자료, 252곳 □ 해석과 한계 확인된 사실 네 선거 모두 분류기가 읽지 못한 표(미분류표)에 보수 후보 표가 상대적으로 많음 크기는 선거마다 다르지만(19대 최대, 20대 최소) 기울기는 거의 일정함 가장 잘 맞는 설명 지지층의 표기 방식 차이 (19대 유승민 K < 1, 군소 후보 K 2.56) 한계: 구·시·군 단위로 합친 자료라서 개인의 행동은 확인할 수 없음 정규성 여부는 부정이나 정상의 증거가 아님 검정 방법 선택과 이상점 판단에 쓰는 도구 남은 과제 투표구 단위 자료로 연령과 표기 방식의 관계 확인 18·20·21대의 후보별 K 확인 (군소 후보 포함) 18·21대도 후보별 K를 계산했습니다. 19대에서 본 패턴이 두 선거에서도 되풀이됩니다. 20대는 후보별 자료가 없어 계산하지 못했습니다. 지금 가진 20대 자료(pe20res)에는 윤석열·이재명 표만 있습니다. □ 18대 후보별 K (문재인 대비, 뉴스타파 251곳) 후보 분류표 득표율 미분류표 득표율 K 전국 합산 K 구·시·군 평균 K > 1인 곳 박근혜 51.5% 59.0% 1.394 1.481 249 / 251 기타 후보 (4명 합) 0.37% 1.42% 4.72 4.92 251 / 251 □ 21대 후보별 K (이재명 대비, 개표상황표 판독 252곳) 후보 성향 분류표 득표율 미분류표 득표율 K 전국 합산 K 구·시·군 평균 K > 1인 곳 김문수 보수 41.4% 46.5% 1.230 1.315 232 / 252 이준석 보수 8.3% 7.3% 0.971 1.022 136 / 252 권영국 진보 0.98% 0.83% 0.926 1.032 115 / 252 송진호 군소 0.10% 0.29% 3.34 3.68 233 / 252 21대 투표 유형별 K (전국 합산) 유형 김문수 이준석 권영국 송진호 관내사전 1.364 0.922 0.983 4.60 선거일 1.278 0.922 0.902 3.36 관외사전 1.137 0.994 0.909 1.74 □ 세 선거에서 되풀이되는 패턴 ○ 보수 후보라도 지지층에 따라 방향이 갈림 고령층 지지가 두터운 보수 후보: K > 1 (박근혜 1.39, 홍준표 1.61, 김문수 1.23) 젊은 층 지지가 두터운 보수 후보: K ≤ 1 (19대 유승민 0.92, 21대 이준석 0.97) 진보 후보: K < 1 (19대 심상정 0.74, 21대 권영국 0.93) ○ 군소 후보는 미분류표에서 크게 늘어남 18대 기타 4.72, 19대 기타 2.56, 21대 송진호 3.34 전국 합산 기준으로 한 선거도 빠짐없이 가장 큼 가능한 설명 투표용지 아래쪽 칸에 찍은 표나 드물게 나오는 표기를 분류기가 덜 읽음 확인 방법: 기호 순서와 투표지 이미지 자료가 필요 ○ 결론 "보수 후보에게 몰아준다"는 설명은 유승민·이준석 결과와 맞지 않음 지지층의 표기 습관과 분류기 판독 특성이 겹친 결과라는 설명이 세 선거 모두에 맞음 표기 습관: 고령층이 칸 경계에 찍는 경우 등 판독 특성: 군소 후보 칸을 덜 읽는 경우 등 한계: 구·시·군 단위로 합친 자료라서 개인의 연령과 표기 방식을 직접 확인할 수 없음 □ 다음 단계 https://k18to21-election2.vercel.app/?view=compare&election=21st https://github.com/sechan9999/K18to21Election2 https://data.newstapa.org/datasets/18-19%EB%8C%80-%EB%8C%80%EC%84%A0-%ED%88%AC%ED%91%9C%EC%A7%80-%EB%B6%84%EB%A5%98%EA%B8%B0-%EC%9A%B4%EC%98%81%EA%B2%B0%EA%B3%BC 18대, 19대 뉴스타파 데이터 https://data.newstapa.org/datasets/18-19대-대선-투표지-분류기-운영결과 https://www.newstapa.org/article/CSoI4 https://www.newstapa.org/article/6DFfH https://www.newstapa.org/article/waaaZ
구분 1 디지털 데이터 → 디지털 신호 2 디지털 데이터 → 아날로그 신호 3 아날로그 데이터 → 디지털 신호 4 아날로그 데이터 → 아날로그 신호 핵심 명칭 회선 부호화 (Line Coding) 디지털 변조 (Digital Modulation) PCM (Pulse Code Modulation) 아날로그 변조 (Analog Modulation) 데이터 디지털 디지털 아날로그 아날로그 최종 신호 디지털 아날로그 디지털 아날로그 쉽게 말하면 0과 1을 전압 변화 로 표현 0과 1을 아날로그 반송파 에 실음 아날로그 정보를 0과 1로 변환 아날로그 정보를 반송파에 실음 주된 목적 디지털 데이터를 전송 매체에 전달할 수 있는 디지털 신호로 변환 디지털 데이터를 무선/대역통과 통신에 적합한 신호로 변환 음성·음악 등의 아날로그 정보를 컴퓨터가 처리할 수 있도록 디지털화 아날로그 정보를 장거리 전송에 적합한 고주파 신호로 변환 대표 기술 NRZ-L ASK PCM AM NRZ-I FSK 표본화 FM Manchester PSK 양자화 PM AMI QAM 부호화 핵심 원리 전압의 레벨 또는 변화 로 0/1 표현 반송파의 진폭·주파수·위상 을 변화 아날로그 신호를 일정 간격으로 측정하고 값을 디지털화 반송파의 진폭·주파수·위상 을 아날로그 정보에 따라 변화 대표 방식 1 NRZ-L 전압 레벨로 0/1 구분 ASK 진폭(Amplitude) 변화 표본화(Sampling) 일정한 시간 간격으로 신호 측정 AM 진폭(Amplitude) 변화 대표 방식 2 NRZ-I 신호의 반전 여부로 데이터 표현 FSK 주파수(Frequency) 변화 양자화(Quantization) 측정값을 일정한 단계로 근사 FM 주파수(Frequency) 변화 대표 방식 3 Manchester 비트 중간에 반드시 신호 전이 발생 PSK 위상(Phase) 변화 부호화(Encoding) 양자화된 값을 2진수로 표현 PM 위상(Phase) 변화 대표 방식 4 AMI 1을 +V/-V로 번갈아 표현 QAM 진폭 + 위상 변화 PCM 전체 과정 Sampling → Quantization → Encoding 특징 구현이 비교적 간단하고 직접적인 디지털 전송 가능 무선통신 등에서 활용하기 좋음 저장·처리·복제에 유리 아날로그 방송 등에 사용 장점 별도의 반송파 없이 디지털 신호로 전송 가능 다양한 변조 방식으로 전송 효율 조절 가능 잡음에 강하고 디지털 저장·처리가 쉬움 아날로그 정보를 자연스럽게 전달 가능 단점/주의점 연속적인 같은 비트에서는 동기화 문제가 발생할 수 있음 변조/복조 과정 필요 표본화·양자화 과정에서 정보 손실 또는 오차 발생 가능 잡음이 누적될 수 있고 디지털 방식보다 처리에 불리 대표 예시 Ethernet 유선 통신 Wi-Fi, LTE, 5G 등 디지털 음성, CD, 음성 녹음 AM/FM 라디오, 아날로그 TV 수신 측 디지털 신호를 원래 데이터로 복원 복조(Demodulation) 하여 디지털 데이터 복원 디지털 데이터를 다시 아날로그 신호로 변환 가능 복조(Demodulation) 하여 원래 아날로그 정보 복원 핵심 키워드 전압 변화 / Line Coding 반송파 / ASK·FSK·PSK·QAM Sampling → Quantization → Encoding 반송파 / AM·FM·PM 시험 암기 디지털 → 디지털 = 회선 부호화 디지털 → 아날로그 = 디지털 변조 아날로그 → 디지털 = PCM 아날로그 → 아날로그 = 아날로그 변조 🔥 시험 직전 초압축 변환 외울 것 세부 방식 디지털 → 디지털 회선 부호화 NRZ, Manchester, AMI 디지털 → 아날로그 디지털 변조 ASK = 진폭 / FSK = 주파수 / PSK = 위상 / QAM = 진폭+위상 아날로그 → 디지털 PCM 표본화 → 양자화 → 부호화 아날로그 → 아날로그 아날로그 변조 AM = 진폭 / FM = 주파수 / PM = 위상 암기 공식 하나만 잡으면: 디→디 = 부호화 / 디→아 = 변조 / 아→디 = PCM / 아→아 = 변조 그리고 ASK·FSK·PSK / AM·FM·PM 은 각각 진폭·주파수·위상 순서로 대응한다고 기억하면 된다.
핵심 한 줄 타이포그래피는 글자를 꾸미는 것이 아니라, 정보의 순서와 브랜드의 목소리를 정하는 것 이다. 01. 타이포를 결정하는 4가지 크기 · 굵기 · 행간 · 자간 크기 Size → 무엇을 먼저 읽게 할지 결정 굵기 Weight → 강조의 정도를 결정 행간 Line height → 문장의 밀도와 가독성을 결정 자간 Letter spacing → 글자의 인상과 밀도를 조절 조정 순서는 보통 크기 → 굵기 → 행간 → 자간 처음부터 모든 값을 만지기보다 이 순서대로 조정하면 훨씬 수월하다. 📌 02. 실무 기본값 처음 시작할 때 참고할 만한 값. 용도 크기 굵기 행간 자간 큰 제목 28–40px 700–800 120–130% -3% 소제목 18–22px 700 130–140% -2% 본문 14–16px 400–500 150–160% -1~-2% 보조 설명 12–13px 400–500 140–150% 0~-1% 버튼 14–16px 700 100% -1% 기억할 것 모바일 본문은 가급적 14px 아래로 내리지 않기 한 화면의 글자 크기는 3단계 정도 면 충분 숫자·가격·할인율처럼 중요한 정보는 주변보다 한 단계 크게 긴 문장은 가운데 정렬보다 왼쪽 정렬 한글 본문의 자간을 지나치게 넓히지 않기 📌 03. 폰트의 ‘얼굴’ 한글 폰트는 크게 보면 먼저 고딕 / 명조 를 구분한다. 고딕 Sans 화면·UI·본문에 가장 일반적 비교적 현대적이고 명확한 인상 명조 Serif 긴 글, 브랜드 스토리, 감성적인 표현 등에 활용 고딕보다 인상이 강하므로 목적이 있을 때 선택 여기에 실무에서는 크게 4가지 얼굴 정도만 기억하면 된다. 고딕 / 명조 / 제목용 / 손글씨 같은 문장이라도 어떤 얼굴을 선택하느냐에 따라 브랜드의 말투가 달라진다. 📌 차분함·믿음 → 안정적인 고딕 친근함·부드러움 → 부드러운 형태 고급스러움·느림 → 명조 급함·센 강조 → 굵고 강한 제목용 서체 📌 04. 폰트는 많이 쓰지 않는다 기본은 폰트 1개 + 굵기 2단계 정도로 시작한다. 예: Pretendard Regular 400 → 본문 Pretendard Bold 700 → 제목·강조 브랜드 성격상 다른 얼굴이 꼭 필요하다면 두 번째 폰트를 추가한다. 예: 고딕 본문 + 명조 제목 하지만 비슷한 고딕 두 개를 섞는 식의 조합은 차이가 명확하지 않으면서 통일성만 깨질 수 있다. → 두 번째 폰트에는 이유가 있어야 한다. 05. 실무에서 시작하기 좋은 폰트 UI·본문용 후보: Pretendard / Noto Sans KR / SUIT / Spoqa Han Sans Neo / IBM Plex Sans KR 제목·강조용은 별도의 디스플레이 폰트를 사용할 수 있고, 긴 글이나 신뢰·감성이 필요한 영역에서는 명조 계열을 고려한다. 단, 실제 프로젝트에서는 예쁘다는 이유만으로 고르지 말고 라이선스부터 확인한다. 확인할 것: 상업적 이용 / 웹·앱 임베딩 / 로고 사용 가능 여부 06. 실제 화면에 적용하는 법 정보를 전부 강조하면 결국 아무것도 강조되지 않는다. 랜딩 화면이라면 기본적으로 큰 제목 한 줄 설명 한 줄 버튼 하나 정도로 핵심을 만든 뒤 나머지 정보는 아래로 내린다. 상세페이지에서도 숫자 → 크게 설명 → 작게 카피 → 한 줄 처럼 역할을 분리한다. 📌 07. 자주 하는 실수 1. 폰트를 3개 이상 사용 → 1개 + 굵기 2단계부터 시작 2. 모든 텍스트를 굵게 → 강조할 곳만 700, 나머지는 400 3. 행간 Auto에 맡김 → 본문 약 150%, 제목 약 120–130%부터 조정 4. 긴 문장을 가운데 정렬 → 긴 글은 왼쪽 정렬 5. 한글 본문의 자간을 넓힘 → 대체로 0~-2% 정도에서 시작 🔖 참고자료 https://noonnu.cc/
특수권한 읽기, 쓰기, 실행 이외의 기능들을 사용 setuid 파일을 실행한 사용자가 파일을 소유한 사용자의 권한으로 실행함 예시 ls -l /etc/shadow 이걸 보면 root도 쓰기 읽기 권한이 없는데 어떻게 수정할까? => passwd 명령어로 수정 which passwd passwd가 실행되는 경로인 /usr/bin/passwd의 권한이 -rwsr-xr-x 소유자의 권한이 s로 설정된것을 확인 root의 권한으로 실행됨 setgid 실행한 사용자의 그룹이 아니라 파일을 소유한 그룹으로 실행됨 디렉토리에 주로 설정: 해당 디렉토리에 생성된 파일들이 그룹 디렉토리의 그룹을 상속받음 ls -ld /run/log/journal => drwxr-sr-x+ 4 root systemd-journal cd /run/log/journal sudo mkdir dirA sudo touch fileA ls -l => 이 파일들의 소유 그룹이 내가 아닌 systemd-journal인것을 확인 sticky bit 디렉토리에 주로 설정: 해당 디렉토리에서 사용자가 자신의 파일만 삭제할 수 있게함 공유게시판에서 내가 쓴 글만 삭제할 수 있는것처럼 rwx만으로는 디렉토리의 권한 설정하기에는 남의 것도 지울 위험 존재 ls -ld /tmp => drwxrwxrwt. 12 root root mkdir /tmp/testdir chmod 777 /tmp/testdir sudo useradd user01 sudo useradd user02 sudo su - user01 touch /tmp/testdir/fileA sudo su - user02 rm /tmp/testdir/fileA chmod +t /tmp/testdir/ sudo su - user01 touch /tmp/testdir/fileA sudo su - user02 rm /tmp/testdir/fileA 결과: 지워지지 않음 setuid, setgid, stikybit 설정법 8비트 4자리로 설정법 숫자 특수 권한 의미 4 SetUID 파일 소유자 권한으로 실행 2 SetGID 파일 그룹 권한으로 실행 1 Sticky Bit 디렉터리에서 남의 파일 삭제 제한 - 이 setuid+setgid+sticybit를 합한 값을 4자리 수 중 맨앞에 설정 - 예제 - 2755 → 755 + SetGID - 1755 → 755 + Sticky Bit - 6755 → 755 + SetUID + SetGID - 7755 → 755 + SetUID + SetGID + Sticky Bit 심볼릭 모드로 설정법 chmod u+s / u-s / g+s / g-s / o+t / o-t 권한으로 find 검색하기 find / -perm -2000 -type f setgid가 있는 파일 찾기 ACL => 일반적으로는 잘 사용하지 않음 특정 사용자나 그룹에 권한을 부여 권한을 상속가능 acl 설치여부 확인법 rpm -q acl sudo dnf install acl acl 설정 전 sudo su - mkdir -p acl/{files,shared} touch acl/files/file1.txt touch acl/files/file2.txt echo "test file" > acl/files/file1.txt useradd user03 ls -l acl/files/file1.txt => -rw-r--r--. 1 root root .으로 끝나는 권한은 acl이 적용안되었다는 뜻 acl 설정 후 setfacl -m u:user01:rw acl/files/file1.txt m은 수정, 사용자는 user01, rw 권한줌 ls -l acl/files/file1.txt => -rw-rw-r--+ 적용되면 권한끝이 +가 됨 getfacl acl/files/file1.txt echo "ACL Test" > acl/files/acl_test.txt ls -l acl/files/acl_test.txt setfacl -m u:user02:r acl/files/acl_test.txt getfacl acl/files/acl_test.txt setfacl -m u:user02:rw,u:user03:r acl/files/acl_test.txt setfacl -m g:nobreak:rw acl/files/acl_test.txt setfacl -m u:user02:rw,u:user03:r,g:nobreak:rw acl/files/acl_test.txt getfacl acl/files/acl_test.txt mask 특정사용자와 그룹이 사용할 수 있는 최대 권한을 설정(상한선 제한) ex)직원 직급마다 출입할 수 있는 곳이 많지만 모두 야간출입을 제한 touch acl/files/mask_test.txt etfacl -m u:user01:rwx,u:user02:rw,u:user03:r acl/files/mask_test.txt getfacl acl/files/mask_test.txt setfacl -m m::r acl/files/mask_test.txt getfacl acl/files/mask_test.txt setfacl -x u:user02 acl/files/mask_test.txt getfacl acl/files/mask_test.txt setfacl -b acl/files/mask_test.txt getfacl acl/files/mask_test.txt 스케줄링 atd 데몬 단일 작업 예약 crond 데몬 주기적 작업 예약 at 명령어 설치 여부 확인법 at rpm -q at at 명령어로 단일 작업 예약 at now +5min echo "hello world" > ~/message.txt ctrl + d로 명령어 입력 마무리 atq 현재 예약된 작업 목록 확인 atrm 1 1번 예약 작업 취소 데몬 서비스이름명 +d 백그라운드에 돌고있는 프로세스가 서비스를 쓸수있게 관리 at 명령어로 설정한 예약 작업이 저장되는 곳 at now +2min echo "hello" > ~/msg.txt ctrl +d ls /var/spool/at => 여기에 저장됨 ps -ef | grep atd