정규화(Normalization)란 무엇이며, 그 목적은 무엇인가요?

김상욱·2024년 12월 14일

정규화(Normalization)란 무엇이며, 그 목적은 무엇인가요?

정규화는 데이터의 중복을 줄이고 데이터 무결성을 유지하기 위해 테이블을 체계적으로 분해하는 과정

목적

  • 동일한 데이터가 여러 곳에 중복 저장되는 것을 방지하여 저장 공간을 효율적으로 사용
  • 데이터의 일관성과 정확성을 보장하여 삽입, 삭제, 갱신 시 발생할 수 있는 이상 현상을 방지
  • 데이터 간의 관계를 명확히 하여 데이터베이스의 설계를 이해하고 쉽고 관리하기 용이하게 만든다.
  • 데이터베이스 구조 변경 시 최소한의 수정으로 대응할 수 있도록 설계

but, 테이블이 많이 분리되면서 조인이 많이 필요해져 쿼리가 복잡해질 수 있다. 또한 데이터 조회 시 조인 연산이 많아지면 성능이 저하될 수 있음. 정규화를 위해서는 신중한 설꼐와 분석이 필요.

제 1정규형 (1NF : First Normal Form)

  • 조건 : 테이블의 각 컬럼이 원자값(Atomic Value)을 가져야 합니다. 즉 하나의 셀에 여러 값이 들어있어서는 안됩니다.
  • 목적 : 중복된 데이터 구조를 제거하고, 각 데이터가 명확하게 분리되도록 합니다.
비정규화된 테이블:
학생ID | 학생이름 | 과목
1      | 홍길동   | 수학, 영어
학생ID | 학생이름 | 과목
1      | 홍길동   | 수학
1      | 홍길동   | 영어

제 2정규형 (2NF : Second Normal Form)

  • 조건 : 1NF을 만족하고, 부분 함수 종속을 제거해야 합니다. 즉, 기본 키의 일부에만 의존하는 속성이 없어야 합니다.
  • 목적 : 복합 키를 사용하는 테이블에서 각 속성이 전체 키에 완전히 의존
비정규화된 테이블:
주문ID | 상품ID | 주문일자 | 상품명
1     | 101   | 2024-01-01 | 노트북
주문 테이블:
주문ID | 상품ID | 주문일자

상품 테이블:
상품ID | 상품명

제 3정규형 (3NF : Third Normal Form)

  • 조건 : 2NF을 만족하고, 이행적 함수 종속을 제거해야 합니다. 즉, 비기본 키 속성이 다른 비기본 키 속성에 종속되지 않아야 합니다.
  • 목적 : 데이터 간의 직접적인 관계만을 유지하여 데이터 무결성을 강화
비정규화된 테이블:
학생ID | 학생이름 | 학과ID | 학과명
1      | 홍길동   | D01   | 컴퓨터공학
학생 테이블:
학생ID | 학생이름 | 학과ID

학과 테이블:
학과ID | 학과명

보이스-코드 정규형 (BCNF: Boyce-Codd Normal Form)

  • 조건 : 3NF를 만족하고 모든 결정자가 후보 키어야 합니다.
  • 목적 : 더욱 엄격한 무결성을 보장하여 특정 복잡한 종속성을 제거

신입이나 취업 준비 중인 Java, Spring 백엔드 개발자 입장에서 정규화(Normalization)와 관련하여 실습할 수 있는 내용은 데이터베이스 설계와 데이터 처리 실습을 중심으로 진행할 수 있습니다. 실습을 통해 데이터베이스 설계 역량과 SQL 작성 능력을 키울 수 있으며, Spring과 연계하여 백엔드 개발 실무에 적용할 수도 있습니다.


1. 정규화를 직접 경험해보기

실습 목표: 비정규화된 데이터를 정규화하는 과정을 통해 정규화의 중요성과 방법을 체험

  • Step 1: 비정규화된 데이터 세트를 준비합니다.

    • 예: 학생, 강의, 수강 정보가 포함된 테이블을 비정규화된 상태로 설정

      CREATE TABLE StudentCourses (
          student_id INT,
          student_name VARCHAR(100),
          course_name VARCHAR(100),
          professor_name VARCHAR(100),
          course_time VARCHAR(100)
      );
      
      INSERT INTO StudentCourses VALUES
      (1, '홍길동', '자바 기초', '이교수', '월요일 10시'),
      (2, '김철수', '데이터베이스', '박교수', '화요일 2시'),
      (1, '홍길동', '데이터베이스', '박교수', '화요일 2시');
  • Step 2: 1NF, 2NF, 3NF 과정을 적용하여 테이블을 분리합니다.

    • 학생 테이블, 강의 테이블, 수강 테이블로 나누고 외래 키를 설정합니다.
  • Step 3: SQL을 통해 분리된 테이블에서 데이터를 삽입/조회합니다.

    • 조인을 사용해 데이터를 원래 상태로 복원하여 조회 결과를 비교합니다.

2. Spring과 연계하여 실습

실습 목표: 정규화된 데이터베이스를 Spring 프로젝트에서 활용해보기

  1. 정규화된 데이터베이스 설계

    • 위에서 분리한 학생, 강의, 수강 테이블을 사용해 MySQL 또는 H2 데이터베이스에 구축합니다.
  2. Spring JPA 엔티티 생성

    • 각 테이블에 대응하는 JPA 엔티티를 설계합니다.
    @Entity
    public class Student {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
    
        @OneToMany(mappedBy = "student")
        private List<CourseRegistration> registrations;
    }
    
    @Entity
    public class Course {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
        private String professor;
    
        @OneToMany(mappedBy = "course")
        private List<CourseRegistration> registrations;
    }
    
    @Entity
    public class CourseRegistration {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @ManyToOne
        @JoinColumn(name = "student_id")
        private Student student;
    
        @ManyToOne
        @JoinColumn(name = "course_id")
        private Course course;
    }
  3. Repository와 서비스 구현

    • JPA Repository를 통해 학생, 강의, 수강 정보를 CRUD 할 수 있도록 구현합니다.
    public interface StudentRepository extends JpaRepository<Student, Long> {}
    public interface CourseRepository extends JpaRepository<Course, Long> {}
    public interface CourseRegistrationRepository extends JpaRepository<CourseRegistration, Long> {}
  4. RestController로 API 구현

    • 학생 정보를 등록하고, 강의에 수강신청하는 API를 작성합니다.
    @RestController
    @RequestMapping("/students")
    public class StudentController {
        private final StudentRepository studentRepository;
    
        @PostMapping
        public ResponseEntity<Student> createStudent(@RequestBody Student student) {
            return ResponseEntity.ok(studentRepository.save(student));
        }
    
        @GetMapping("/{id}")
        public ResponseEntity<Student> getStudent(@PathVariable Long id) {
            return ResponseEntity.ok(studentRepository.findById(id).orElseThrow());
        }
    }
  5. 테스트 및 검증

    • Postman이나 Swagger를 활용해 API를 테스트합니다.
    • 정규화된 데이터 구조에서 데이터를 효과적으로 CRUD하는 과정을 경험합니다.

3. 비정규화 실습

실습 목표: 비정규화의 필요성을 이해하고 성능 최적화 경험

  1. 비정규화 테이블 설계

    • 학생 이름과 강의 이름을 다시 중복 저장하는 비정규화된 테이블을 설계합니다.
  2. 정규화 테이블과 성능 비교

    • 데이터를 대량으로 삽입한 후 정규화된 테이블과 비정규화된 테이블의 조회 성능을 비교합니다.
    • SQL 쿼리 실행 시간을 측정하여 조인 사용 유무가 성능에 미치는 영향을 분석합니다.

4. 정규화와 비정규화의 장단점 문서화

실습 목표: 설계 결정의 이유를 기록하며 설계 역량 강화

  • 정규화된 테이블과 비정규화된 테이블의 장단점을 비교하여 Markdown이나 블로그에 문서화합니다.
  • 예: "정규화된 테이블은 데이터 일관성을 보장하지만, 조인 비용이 커질 수 있다."

5. 개인 프로젝트에 응용

  • 프로젝트 제안: "학생 관리 시스템" 또는 "강의 수강 관리 웹 애플리케이션"을 설계하여 정규화를 실무에 적용
  • 정규화된 데이터베이스 설계 후 Spring과 연동하여 RESTful API 구현
  • 관리 페이지를 만들어 데이터를 조회하고 수정하는 기능 제공

추가 팁

  • SQL 학습 플랫폼 활용: LeetCode의 Database 문제를 풀며 SQL 실력을 키우고, 정규화된 테이블 설계를 이해합니다.
  • Mock 데이터 생성: Faker 라이브러리를 사용하여 대량의 테스트 데이터를 생성합니다.
  • 프로파일링 도구: Spring Boot의 Actuator와 SQL 로그를 활용하여 성능을 분석하고 튜닝 경험을 쌓습니다.

이러한 실습을 통해 정규화된 데이터 설계와 실무 적용 능력을 동시에 키울 수 있습니다!

0개의 댓글