<목차>
1. 결합도 유형 (내.공.외.제.스.자) Bad → Good
2. Anotation
- @Controller @RequestMapping @RequestParam @RequestHeader
- @PathVariable @CookieValue @ModelAttribute @ResponseBody
- @RequestBody @Repository @Service @Scheduled
3. 데이터베이스 트랜잭션
- 4가지 성질 (ACID) | 모델링 | 인터페이스 설계 | 세부 설계
4. 논리-물리 데이터 모델 변환
5. 가상테이블 뷰 (View)
6. 관계대수 · 관계해석 · SQL SELECT
7. 스키마 (Schema)
- 스키마 3계층 (외부-개념-내부)
- 3계층 스키마 vs 데이터베이스 설계 3단계 (개념-논리-물리)
▶ 결합도 유형 (내.공.외.제.스.자) Bad->Good
유형 | 설명 |
자료 결합도 (Data Coupling) |
모듈 간의 인터페이스로 값이 전달되는 경우 |
스탬프 결합도 (Stamp Coupling) |
모듈 간의 인터페이스로 배열이나 오브젝트, 스트럭처 등이 전달되는 경우 |
제어 결합도 (Control Coupling) |
단순 처리할 대상인 값만 전달되는 게 아니라 어떻게 처리를 해야 한다는 제어 요소가 전달되는 경우 |
외부 결합도 (External Coupling) |
어떤 모듈에서 선언한 데이터(변수)를 외부의 다른 모듈에서 참조하는 경우 |
공통 결합도 (Common Coupling) |
파라미터가 아닌 모듈 밖에 선언되어 있는 전역 변수를 참조하고, 전역 변수를 갱신하는 식으로 상호 작용하는 경우 |
내용 결합도 (Content Coupling) |
다른 모듈 내부에 있는 변수나 기능을 다른 모듈에서 사용하는 경우 |
1. 내용 결합도 (Content Coupling) — 가장 나쁨 (Bad)
- 정의: 한 모듈이 다른 모듈의 내부(변수, 코드 등)를 직접 접근하는 경우
- 문제점: 내부 구현이 바뀌면 접근한 모듈도 함께 수정해야 함
// 나쁜 예시
class ModuleA {
public int value = 10;
}
class ModuleB {
void printValue(ModuleA a) {
System.out.println(a.value); // 내부 변수 직접 접근 -> 내용 결합도
}
}
2. 공통 결합도 (Common Coupling)
- 정의: 여러 모듈이 전역 변수(global variable)를 공유
- 문제점: 전역 변수의 변경이 모든 모듈에 영향을 줌
// 나쁜 예시
public class Shared {
public static int sharedValue = 0;
}
class ModuleA {
void update() {
Shared.sharedValue = 10; // 공통 변수 변경
}
}
class ModuleB {
void read() {
System.out.println(Shared.sharedValue); // 같은 변수 참조
}
}
3. 외부 결합도 (External Coupling)
- 정의: 모듈이 외부 포맷(파일, DB 포맷 등)에 의존할 때
- 문제점: 외부 변경이 모듈 동작에 영향을 줌
// 예시: 외부 파일 포맷에 의존
class Module {
void loadData(String filePath) {
// CSV 포맷 고정
// 만약 JSON으로 바뀌면 이 코드 전체 변경 필요
}
}
4. 제어 결합도 (Control Coupling)
- 정의: 모듈이 제어 흐름(플래그, 조건)을 인자로 전달
- 문제점: 한 모듈이 다른 모듈의 실행 흐름을 제어함
// 예시
class Printer {
void print(boolean isDetailed) {
if (isDetailed) {
System.out.println("상세 출력");
} else {
System.out.println("간단 출력");
}
}
}
5. 스탬프 결합도 (Stamp Coupling)
- 정의: 필요 이상으로 많은 데이터 구조(객체 전체)를 넘기는 경우
- 문제점: 특정 필드만 쓰는데 객체 전체를 넘김 -> 불필요한 의존성
class Customer {
String name;
String address;
String phone;
}
class Service {
void greet(Customer customer) {
System.out.println("Hello " + customer.name); // name만 필요함
}
}
6. 자료 결합도 (Data Coupling) — 가장 이상적 (Good)
- 정의: 모듈이 순수한 데이터를 인자로 주고받음 (필요한 것만)
- 장점: 결합도 낮음, 독립적임
class Calculator {
int add(int a, int b) {
return a + b; // 필요한 데이터만 전달
}
}
▶ Anotation
종료 | 설명 |
@Controller | 스프링 MVC에서 컨트롤러 객체임을 명시. 웹 요청을 처리하는 클래스에 선언 |
@RequestMapping | 클라이언트에서 보낸 URI에 매핑되어 있을 때 명시. 주로 URL 요청을 컨트롤러와 연결할 때 사용 |
@RequestParam | HTTP 요청 파라미터 값을 파라미터 변수에 할당. 주로 단일 파라미터 전달 시 사용 |
@RequestHeader | HTTP 요청 헤더에서 정보를 추출할 때 사용 |
@PathVariable | URI의 경로 변수를 받을 때 사용. 주로 RESTful URI 경로를 처리할 때 유용 |
@CookieValue | 요청의 쿠키 정보 중 특정 값을 컨트롤러 파라미터로 받을 때 사용 |
@ModelAttribute | 자동으로 모델 객체를 바인딩할 때 사용. 요청 파라미터를 객체에 바인딩함 |
@ResponseBody | 메서드의 리턴값을 HTTP 응답 본문으로 전송할 때 사용. 주로 JSON/XML 응답 생성에 사용 |
@RequestBody | HTTP 요청의 본문을 객체로 매핑할 때 사용. 주로 JSON이나 XML 데이터를 자바 객체로 매핑할 때 사용 |
@Repository | DAO 클래스에 선언하여 데이터베이스 연산과 관련된 클래스를 명시함 |
@Service | 비즈니스 계층의 서비스 클래스에 사용 |
@Scheduled | 스프링에서 지원하는 배치 작업을 정의할 때 사용. 정해진 시간 또는 조건에 따라 메서드를 실행함 |
연결 순서
HTTP 요청이 들어오면 아래처럼 흐름이 이어짐
- @Controller → 사용자의 요청을 수신
- @RequestMapping 또는 @PostMapping, @GetMapping 등 → URL 매핑
- @RequestParam, @RequestBody, @ModelAttribute 등으로 요청 데이터 수신
- 내부적으로 @Service 호출 → 비즈니스 처리
- DB 작업은 @Repository에서 처리
- 결과를 View로 응답
회원 등록 시스템 예제(회원 가입 + 회원 수 출력)
- 회원이 JSON 형식으로 회원가입 요청을 보냄
- 컨트롤러에서 요청을 받아 서비스로 전달
- 서비스는 DB에 저장 요청
- DAO는 DB에 회원 정보 저장
- 스케줄러가 매일 오전 9시에 회원 수를 출력
1. User 모델 클래스
public class User {
private String name;
private int age;
private String email;
// 생성자, getter, setter 생략
}
2. UserController — 사용자 요청 처리 컨트롤러
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
@Controller // 컨트롤러 지정
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
// 사용자 등록
@PostMapping("/users/register") // URL 매핑
@ResponseBody // 문자열을 응답 본문으로 바로 반환
public String registerUser(
@RequestBody User user, // JSON 본문을 User 객체로 매핑
@RequestHeader("User-Agent") String userAgent, // 요청 헤더 읽기
@CookieValue(value = "ref", defaultValue = "none") String referral // 쿠키값
) {
System.out.println("User-Agent: " + userAgent);
System.out.println("Referral Code: " + referral);
userService.register(user);
return "회원가입 완료";
}
// 회원 ID로 정보 조회
@GetMapping("/users/{userId}")
@ResponseBody
public User getUserById(@PathVariable("userId") Long id) {
return userService.getUser(id); // PathVariable로 URL 경로에서 값 추출
}
// 쿼리 파라미터로 이름 검색
@GetMapping("/users/search")
@ResponseBody
public String searchUser(@RequestParam("name") String name) {
return "검색된 이름: " + name;
}
// 폼 방식으로 데이터 전달
@PostMapping("/users/form-register")
@ResponseBody
public String formRegister(@ModelAttribute User user) { // 폼데이터 자동 바인딩
userService.register(user);
return "폼 등록 완료";
}
}
3. UserService — 비즈니스 로직
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service // 비즈니스 계층
public class UserService {
private final UserRepository userRepository;
private final Map<Long, User> cache = new HashMap<>();
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void register(User user) {
userRepository.save(user);
}
public User getUser(Long id) {
// 간단한 캐시 처리
return cache.computeIfAbsent(id, userRepository::findById);
}
}
4. UserRepository — DAO 계층
import org.springframework.stereotype.Repository;
@Repository // DB 작업 담당
public class UserRepository {
// DB 연결은 생략, 단순 출력으로 대체
public void save(User user) {
System.out.println("DB에 저장: " + user.getName());
}
public User findById(Long id) {
return new User("가짜이름", 20, "fake@email.com");
}
}
5. UserScheduler — 배치 작업 처리 클래스
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class UserScheduler {
// 매일 오전 9시에 실행
@Scheduled(cron = "0 0 9 * * ?")
public void printUserCount() {
System.out.println("9시입니다. 현재 사용자 수를 확인하세요!");
}
}
정리된 흐름 구조
설명 | Anotation | |
1 | 사용자가 /users/register로 JSON 요청 전송 | @RequestBody, @RequestHeader, @CookieValue |
2 | 컨트롤러가 요청 처리 | @Controller, @RequestMapping, @ResponseBody |
3 | 서비스 로직 실행 | @Service |
4 | DAO에서 DB 저장 | @Repository |
5 | /users/{id} 요청으로 사용자 조회 | @PathVariable |
6 | /users/search?name=Kim 검색 | @RequestParam |
7 | 폼 전송시 사용자 등록 | @ModelAttribute |
8 | 매일 아침 9시 자동 실행 | @Scheduled |
▶ 데이터베이스 트랜잭션
트랜잭션의 개념 → 모델링 → 인터페이스 설계 → 세부 설계
1. 트랜잭션이란?
데이터베이스에서 하나의 작업 단위
예: “A가 B에게 1만 원을 송금하는 작업”
- A의 계좌에서 1만 원을 차감
- B의 계좌에 1만 원을 더함
이 두 작업은 무조건 함께 성공하거나, 함께 실패해야함.
2. 트랜잭션의 4가지 성질 (ACID)
- Atomicity (원자성): 전부 성공하거나 전부 실패해야 함
- Consistency (일관성): 트랜잭션이 실행된 후에도 데이터 무결성이 유지되어야 함
- Isolation (격리성): 동시에 여러 트랜잭션이 실행되어도 서로 영향을 주지 않아야 함
- Durability (지속성): 성공한 트랜잭션의 결과는 영구적으로 저장되어야 함
3. 예제 코드 (Java + SpringBoot + JPA)
@Service
public class BankService {
@Autowired
private AccountRepository accountRepository;
// 트랜잭션 처리
@Transactional
public void transferMoney(Long fromId, Long toId, int amount) {
// 1. 출금
Account fromAccount = accountRepository.findById(fromId).orElseThrow();
fromAccount.withdraw(amount);
// 2. 입금
Account toAccount = accountRepository.findById(toId).orElseThrow();
toAccount.deposit(amount);
// 3. 여기서 예외 발생하면 위 작업 모두 롤백됨
}
}
4. 트랜잭션 모델링이란?
모델링 단계에서는 시스템에서 어떤 작업이 트랜잭션 단위가 될지를 정의함.
예시: 쇼핑몰에서 주문 처리 트랜잭션 모델링
- 재고 차감
- 결제 처리
- 주문 내역 저장
이 세 가지 작업은 하나의 트랜잭션으로 묶어야 함 → 이게 트랜잭션 모델링
5. 트랜잭션 인터페이스 설계란?
트랜잭션을 처리할 Service 계층의 메서드 구조를 잡는 것
public interface OrderService {
void placeOrder(OrderRequestDto request);
}
이 메서드 내부에서 다음 작업이 수행됨:
- 재고 감소
- 결제 처리
- 주문 정보 저장
→ 즉, 트랜잭션의 인터페이스는 트랜잭션을 실행하는 비즈니스 로직의 입구
6. 트랜잭션 세부 설계란?
세부 설계는 트랜잭션을 이루는 세부 단위 작업을 구체적으로 정의하는 것
@Transactional
public void placeOrder(OrderRequestDto request) {
// 1. 상품 재고 감소
productService.reduceStock(request.getProductId(), request.getQuantity());
// 2. 결제 처리
paymentService.processPayment(request.getUserId(), request.getAmount());
// 3. 주문 저장
orderRepository.save(new Order(...));
}
- 각 단계의 세부 로직을 어떻게 구현할지, 예외 처리 시 어떤 방식으로 롤백할지까지 포함해서 설계하는 게 세부 설계임
예제 전체 흐름
public interface OrderService {
void placeOrder(OrderRequestDto request); // 트랜잭션 인터페이스
}
@Service
public class OrderServiceImpl implements OrderService {
@Transactional // 트랜잭션 처리 시작
public void placeOrder(OrderRequestDto request) {
// 세부 설계
productService.reduceStock(request.getProductId(), request.getQuantity());
paymentService.processPayment(request.getUserId(), request.getAmount());
orderRepository.save(new Order(...));
}
}
▶ 논리 - 물리 데이터 모델 변환
- Entity를 Table로 변환
- 속성을 컬럼으로 변환
- Primary UID를 Primary Key로 변환
- Secondary(Alternate) UID를 Unique Key로 변환
- Relationship을 Foreign Key로 변환
- Business Constraints를 Check Constraints로 변환
👉 논리 모델: "무엇을 저장할 것인가"에 집중 (개념적 모델)
👉 물리 모델: "어떻게 저장할 것인가"에 집중 (현실에 적용되는 구조)
1. Entity → Table
- Entity(엔티티): 현실 세계의 개념적인 객체 (예: 고객, 주문)
- Table(테이블): 데이터베이스에 실제로 저장되는 표 형태의 구조
- 변환 이유: 실제로 데이터를 저장하려면 테이블 형태로 만들어야 하니까
2. 속성 → 컬럼
- 속성(Attribute): 엔티티의 특성 (예: 고객 이름, 주문일자)
- 컬럼(Column): 테이블에서 실제 데이터가 저장되는 필드
- 변환 이유: 속성들이 테이블에 저장될 공간이 필요하므로 컬럼으로 바뀜
3. Primary UID → Primary Key
- Primary UID(식별자): 각 엔티티 인스턴스를 구별하는 고유한 속성
- Primary Key(기본 키): 테이블에서 각 행을 유일하게 식별하는 컬럼
- 변환 이유: 테이블에서도 고유값으로 각 행을 구분해야 하기 때문
4. Secondary UID → Unique Key
- Secondary UID(보조 식별자): Primary는 아니지만 유일한 값을 갖는 속성
- Unique Key(유일 키): 중복이 허용되지 않는 컬럼
- 변환 이유: 특정 컬럼이 중복되지 않도록 제약을 걸기 위해 사용
5. Relationship → Foreign Key
- Relationship(관계): 두 엔티티 간의 연결 (예: 주문은 고객과 관련 있음)
- Foreign Key(외래 키): 한 테이블이 다른 테이블의 Primary Key를 참조함
- 변환 이유: 테이블 간의 연결 관계를 표현해야 하므로 외래 키로 구현
6. Business Constraints → Check Constraints
- Business Constraints(업무 제약조건): 업무 규칙 (예: 수량은 0보다 커야 함)
- Check Constraints(체크 제약조건): 조건을 설정해 데이터 유효성 보장
- 변환 이유: 잘못된 데이터가 들어오지 않게 DB에서 직접 제한할 수 있도록 함
▶ 가상테이블 뷰
1. 복잡한 쿼리 재사용 및 간소화
- 예를 들어, 5개의 테이블을 조인해서 만들어야 하는 결과가 있다고 해봐.
- 이걸 자주 써야 한다면 매번 긴 SQL을 복붙하는 건 비효율적
- 이럴 때 뷰를 만들어두면 SELECT * FROM order_summary_view;처럼 간단하게 사용 가능
2. 보안과 접근 제어
- 사용자가 전체 테이블을 보면 안 되는 경우가 있음. 예를 들어 salary나 admin_flag 같은 필드는 숨겨야함
- 뷰를 통해 보여줄 필드만 노출하면 테이블 전체를 직접 접근하지 않아도 됨
CREATE VIEW public_employee_info AS
SELECT name, department
FROM employees
WHERE role != 'admin';
//이렇게 하면 뷰를 통해 보는 사람은 이름이랑 부서밖에 못봄
3. 읽기 전용 데이터 제공
- 데이터를 직접 수정하면 안 되는 상황에서, 뷰를 통해 읽기 전용 인터페이스처럼 쓸 수 있음
- 특히 복잡한 보고서, 분석용 데이터 등은 뷰로 만들어두면 실수로 데이터 변경하는 일도 막을 수 있음
4. 유지보수 및 설계 유연성
- 테이블 구조가 바뀌더라도 뷰 구조는 동일하게 유지 가능함
- 애플리케이션은 뷰만 보고 작동하니까, 내부 DB 설계가 바뀌어도 앱 로직 수정 없이 계속 쓸 수 있음
5. 성능 최적화 (경우에 따라)
- 일부 DBMS는 물리적 뷰(materialized view)를 제공해서 뷰 결과를 캐싱해둬서 성능을 높일 수 있음
- 단, 일반 뷰는 단순 SQL 재사용일 뿐, 성능 향상 목적은 아님
6. 예제
-- 주문 테이블
CREATE TABLE orders (
order_id INT,
customer_id INT,
product_name VARCHAR(50),
amount INT
);
-- 고객 테이블
CREATE TABLE customers (
customer_id INT,
customer_name VARCHAR(50)
);
- 뷰를 만들지 않고 매번 조인해서 사용하는 방식 (안 쓸 때)
-- 고객 이름과 주문 내역을 보고 싶을 때
SELECT o.order_id, c.customer_name, o.product_name, o.amount
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.amount > 100;
- 이 긴 쿼리를 계속 반복해서 써야 함
- 유지보수 힘듦 (조인 방식 바뀌면 전부 수정해야 함)
- 뷰를 만들어서 사용하는 방식 (쓸 때)
-- [설명] 뷰를 만들어두면 복잡한 쿼리를 이름으로 재사용 가능함
CREATE OR REPLACE VIEW high_value_orders AS
SELECT o.order_id, c.customer_name, o.product_name, o.amount
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.amount > 100;
- OR REPLACE는 같은 이름의 뷰가 있으면 덮어씀
- 에러 없이 기존 뷰를 업데이트할 수 있음 (DROP 안 해도 됨)
비교
항복 | 뷰 안 쓸 때 | 뷰 쓸 때 |
재사용성 | 없음, 매번 쿼리 작성 | 높음, SELECT 한 줄로 끝 |
가독성 | 복잡한 쿼리 반복 | 간단한 이름으로 호출 |
보안 | 전체 테이블 접근 필요 | 필요한 정보만 제한 노출 가능 |
유지보수 | 모든 쿼리 수정 필요 | 뷰 하나만 수정하면 반영됨 |
실행 성능 | 동일 (뷰도 결국 쿼리임) | 단, 머티리얼라이즈드 뷰는 캐싱 가능 |
▶ 관계대수, 관계해석, SQL SELECT
1. 관계대수(Relational Algebra) = "절차 중심"
어떻게 데이터를 찾을지를 단계별로 절차적으로 말해주는 방식
예시 비유
- "냉장고에서 우유를 꺼내려면 → 문 열고 → 맨 오른쪽 선반 보고 → 우유 꺼내"
- 데이터를 어떻게 꺼낼지 알고리즘처럼 말함
관계대수 구문 예시 (σ: 선택, π: 투영, ⨝: 조인)
π name (σ department='영업부' (Employee))
- '영업부' 직원만 고르고 → 이름만 출력
2. 관계해석(Relational Calculus) = "선언 중심"
무엇을 원하는지 조건만 말하는 방식, 절차는 신경 안 씀
예시 비유
- "나는 영업부 직원 이름을 원해"
- 어떻게 가져올지는 DB가 알아서 결정
관계해석 구문 예시 (튜플 관계 해석)
{ e.name | Employee(e) AND e.department = '영업부' }
- 'e'는 Employee의 튜플이고
- 조건을 만족하는 e의 name만 가져와
3. SQL SELECT문 = "관계해석 기반 + 관계대수 절차 가미"
선언형처럼 보이지만, 내부적으로는 관계대수처럼 동작
그래서 SQL은 관계해석을 바탕으로 한 언어지만, DBMS는 내부에서 관계대수로 처리함
SQL 예시
SELECT name FROM Employee WHERE department = '영업부';
- 보는 입장에선 선언형: “이 조건 만족하는 name 달라”
- 실제 동작은 절차적: 필터 → 컬럼 추출 순서로 처리함(내부적으로 "관계대수"라는 절차적 언어로 바꿔서 실행)
4. 정리
항목 | 관계대수 | 관계해석 | SQL (SELECT) |
방식 | 절차적 | 선언적 | 선언적 (내부는 절차적) |
무엇에 초점 | 어떻게 구할까 | 무엇을 원하나 | 무엇을 원하나 (자동 최적화) |
난이도 | 알고리즘적 | 수학적 논리 느낌 | 쉬움 (실무용) |
실무 사용 | × (이론 전용) | × (이론 전용) | O (우리가 쓰는 SQL) |
- 관계대수: 데이터를 어떻게 가져올지 단계적으로 지시
- 관계해석: 필요한 데이터 조건만 정의하고 방법은 신경 안 씀
- SQL SELECT: 조건만 쓰지만, 내부는 관계대수처럼 실행됨
5. 관계대수/관계해석을 배우는 이유
1) SQL의 본질과 동작 방식 이해
- SQL을 잘 쓰려면, 백엔드에서 어떤 일이 벌어지는지 이해해야 함
- 어떤 조건이 선 필터링되고, 어떤 조건이 조인 후 필터링되는지 안 보이면 성능 튜닝 못 함
2) 쿼리 최적화와 실행 순서 파악
이 쿼리
SELECT name FROM Employee WHERE department = '영업부';
와
SELECT name FROM (SELECT * FROM Employee WHERE department = '영업부');
결과는 같지만, 내부 처리 순서가 달라질 수 있음.
→ 관계대수 관점에서 보면, 필터 후 projection이 빠름
→ 반대로 하면 불필요한 데이터가 메모리에 오래 남음 → 느려짐
3) DB 설계와 질의 최적화
- 실제 실무에서도 성능 이슈 날 때 EXPLAIN 실행계획 보고 튜닝하는데
- 관계대수 개념이 있어야 인덱스 언제 쓰이고 안 쓰이는지, 조인이 어떻게 최적화되는지 이해 가능함
▶ 스키마(Schema)
1. 스키마는 '설계도'다.
건물을 짓기 전에 그려놓는 도면처럼, 어떤 데이터가 어떤 식으로 저장될지 정해놓는 설계 문서임
예를 들어, 우리가 '고객 정보'를 저장하려고 한다면, 이렇게 생긴 표가 필요할 수 있음
이런 표를 테이블(table)이라고 하고, 이 테이블이 어떤 칸(컬럼, 즉 "필드")으로 구성되는지 정의한 게 바로 스키마임
2. 스키마는 무엇을 정의할까?
테이블 이름 | 컬럼의 이름과 타입 | 기본키,외래키 | 제약 조건
1) 테이블 이름
- 예: Customer, Product, Order
2) 컬럼(열)의 이름과 타입
- 예: 이름(name)은 글자(String), 가격(price)은 숫자(Number), 날짜(created_at)는 날짜(Date)
3) 키(Primary Key, Foreign Key)
- Primary Key (기본키): 데이터의 고유한 식별자 (예: 고객번호)
- Foreign Key (외래키): 다른 테이블과 연결된 키 (예: 주문 테이블의 고객번호 → 고객 테이블과 연결)
4) 제약 조건(Constraints)
- 예: 이름은 꼭 있어야 한다(NOT NULL), 나이는 0보다 커야 한다(CHECK)
3. 예시
CREATE TABLE Customer (
customer_id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
phone VARCHAR(20),
address TEXT
);
이건 "Customer"라는 테이블을 만드는 SQL 문장으로 스키마의 한 부분
여기서 설계한 내용은 다음과 같다:
- customer_id: 숫자고, 중복되면 안 되는 고유 ID
- name: 글자 100자까지 가능, 반드시 입력해야 함
- phone, address: 전화번호, 주소
4. 왜 중요한가?
- 데이터를 일관성 있게 저장하기 위해
- 데이터 구조를 팀원들이 공통으로 이해하기 위해
- 프로그램이 데이터를 신뢰하고 처리할 수 있게 하기 위해
5. 스키마 3계층(외부-개념-내부)
- 외부 스키마: 사용자(또는 앱)가 보는 데이터 모습
- 개념 스키마: 전체 데이터베이스의 논리적 설계
- 내부 스키마: 데이터가 실제로 저장되는 방식
1) 외부 스키마 (External Schema)
사용자가 도서관에서 책을 빌릴 때, "제목, 저자, 대출 가능 여부"만 보는 것
- "회원"은 책 제목, 저자만 볼 수 있음
- "관리자"는 가격, 입고일 등 더 많은 정보도 볼 수 있음
- 사용자마다 필요한 정보만 보여줌
- 여러 외부 스키마가 있을 수 있음 (예: 직원용, 일반 사용자용)
2) 개념 스키마 (Conceptual Schema)
도서관 전체에 어떤 정보(책, 대출 기록, 회원 정보 등)를 저장하는지 정의하는 것
- 책(Book), 회원(Member), 대출(Loan) 테이블이 어떻게 연결되는지
- 어떤 속성들이 있는지 정의
- 데이터 전체의 논리적인 구조
- 테이블 관계, 제약 조건 등을 포함
3) 내부 스키마 (Internal Schema)
책이 실제로 도서관 어디 선반에, 어떤 방식으로 저장돼 있는지를 설계하는 것
- 어떤 책이 어느 선반에, 어떤 분류로 저장되는지
- 데이터를 빠르게 검색하기 위해 인덱스를 걸어놓는다든가
- 데이터가 물리적으로 저장되는 방식
- 인덱스, 파일 포맷, 저장 경로 등
정리
구분 | 설명 | 예시 |
외부 스키마 | 사용자/앱이 보는 데이터 구조 | 고객용 뷰, 관리자용 뷰 |
개념 스키마 | 전체 데이터의 논리적 설계 | 전체 ERD 구조 |
내부 스키마 | 데이터 저장의 실제 구현 (물리적 구조) | 인덱스, 저장 파일 구조 |
왜 나누는 걸까?
- 시스템을 유지보수하기 쉽게 만들기 위해
- 내부 구조를 바꿔도 사용자 화면은 그대로 유지되게 하기 위해
- 보안과 최적화를 각각 다르게 적용하기 위해
📍3계층 스키마 vs 데이터베이스 설계 3단계
구분 | 3계층 스키마 구조 | 데이터베이스 설계 3단계 |
초점 | "시스템이 데이터에 접근하는 방식" | "데이터를 어떻게 만들고 구조화할지" |
관점 | 사용자 ↔ 시스템 내부 구조 | 설계자 입장에서 데이터 생성 단계 |
단계 | 외부 / 개념 / 내부 스키마 | 개념 / 논리 / 물리 설계 |
1) 3계층 스키마 구조
시스템이 어떻게 데이터를 표현하고 저장할지 구분하는 구조
- 데이터 표현을 계층화해서 독립성 보장 (화면은 그대로, 내부 구조만 변경 가능)
- 외부 스키마: 사용자 입장에서 보이는 데이터 (뷰)
- 개념 스키마: 전체 데이터베이스 구조 (ERD, 관계 등)
- 내부 스키마: 실제 저장 구조 (파일, 인덱스, 저장소)
2) 데이터베이스 설계 3단계
설계자가 처음부터 DB를 어떻게 만들지 순서대로 구성하는 과정
- 현실 세계의 정보를 어떻게 정확하고 성능 좋게 데이터베이스로 구현할지 설계
- 개념 설계: ERD 그리기, 엔터티와 관계 정의
- 논리 설계: 테이블/컬럼/키 설계 (RDBMS에 맞게)
- 물리 설계: 인덱스, 파티션, 저장소 구조 등 성능 최적화
정리
항목 | 3계층 스키마 | DB 설계 3단계 |
중심 질문 | "어떻게 저장하고 보여줄까?" ( 데이터 표현 방식) |
"어떻게 만들어야 할까?" (데이터 만드는 과정) |
건물 비유 | 건물 도면(설계도), 사용자의 시각, 내부 배선 |
기획 → 설계도 작성 → 시공 작업
|
사용 시점 | DB가 작동할 때 적용 | DB를 만들기 전에 적용 |
결과물 | 사용자 뷰, 저장 구조 | ERD, DDL 스크립트, 인덱스 설정 |
참고
Q1: DB 설계 단계에서 ERD는 어느 단계에 포함될까?
정답: 개념 설계 단계
- ERD(Entity-Relationship Diagram)는 현실 세계의 개체(사람, 상품, 주문 등)와 이들 사이의 관계(소유, 구매 등)를 시각적으로 표현한 거야.
- 개념 설계는 "무엇을 저장할지"에 집중하는 단계라서, ERD를 작성하는 것이 핵심 작업이야.
예시:
고객(Customer), 주문(Order), 상품(Product)이 있다면
→ 각각을 엔터티로,
→ 고객이 주문한다, 주문이 상품을 포함한다는 관계를 관계(Relationship)로 표현
요약:
- ERD는 개념 설계의 핵심 도구
- 아직 RDBMS(SQL, MySQL 등)에 맞게 테이블로 변환하지 않은 상태
Q2: 내부 스키마와 물리 설계는 어떻게 연결되어 있을까?
정답: 내부 스키마 = 물리 설계의 구현 결과
- 내부 스키마는 데이터가 실제로 어떻게 저장되는지를 시스템 관점에서 표현한 것이고,
- 물리 설계는 설계자 입장에서 성능과 저장 공간을 고려해서 저장 방식을 결정하는 것이야.
즉,
물리 설계에서 결정한 내용
→ 인덱스를 어떻게 걸고, 어떤 칼럼에 파티션을 나누고, 어떤 형식으로 저장할지
→ 이것이 내부 스키마에 반영되어 구현되는 거야.
예시:
물리 설계자가 “상품 테이블의 상품명 컬럼에 인덱스를 걸자”고 하면
→ 내부 스키마에서는 그 인덱스를 활용한 실제 저장 구조를 정의하고 운영에 반영함
'개발인생 > Certificate' 카테고리의 다른 글
정처기 실기[기출1] (0) | 2025.04.18 |
---|---|
정처기 실기[이론3] (0) | 2025.04.16 |
정처기 실기[이론-정보보안] (0) | 2025.04.16 |
정처기 실기[이론1] (8) | 2025.04.08 |
정처기 실기[프로그래밍언어] (1) | 2025.04.08 |