연습: 데이터베이스 리팩터링 기술 적용

Visual Studio Premium 또는 Visual Studio Ultimate에서 리팩터링을 사용하면 데이터베이스 스키마를 디자인하고 업데이트할 때 수행해야 하는 반복적이고 오류가 발생하기 쉬운 작업의 수를 줄일 수 있습니다. 예를 들어 데이터베이스 개체 이름을 변경해야 하거나 개체를 다른 스키마로 이동해야 하는 경우 리팩터링을 사용하여 해당 개체에 대한 참조를 업데이트할 수 있습니다. 이 방법을 사용하면 데이터베이스 디자인에 대한 일상적인 변경 작업의 속도와 정확도를 모두 향상시킬 수 있습니다.

이 연습에서는 데이터베이스 개발의 일반적인 시나리오를 보여 줍니다. 기존 데이터베이스에 기능을 추가하려면 초기 구현을 수행한 다음 다른 팀 멤버와 함께 구현을 검토해야 합니다. 검토 과정에서 몇 가지 문제가 확인되면 변경 내용을 체크 인하기 전에 이를 처리해야 합니다. 그런 다음 다양한 리팩터링 기술을 사용하여 스키마를 변경합니다.

이 연습에서는 다음 작업을 수행합니다.

  • 데이터베이스 스키마 가져오기

  • 일반적인 데이터베이스 개발 작업 구현

  • 코딩 오류 수정

  • 개발 작업 완료

  • 코드 검토 피드백 처리

사전 요구 사항

이 연습을 완료하려면 다음이 필요합니다.

  • Visual Studio Premium 또는 Visual Studio Ultimate

  • AdventureWorks2008 데이터베이스가 설치된 데이터베이스 서버에 대한 읽기 전용 액세스 권한

데이터베이스 스키마 가져오기

팀 환경에서는 일반적으로 스키마를 변경하기 전에 기존 프로젝트를 버전 제어 시스템에서 체크 아웃합니다. 이 연습의 경우 데이터베이스 프로젝트를 만들고 AdventureWorks2008 샘플 데이터베이스에서 스키마를 가져옵니다.

데이터베이스 프로젝트를 만들려면

  1. 파일 메뉴에서 새로 만들기를 가리킨 다음 프로젝트를 클릭합니다.

    새 프로젝트 대화 상자가 나타납니다.

  2. 설치된 템플릿에서 데이터베이스 노드를 확장하고 SQL Server 노드를 클릭합니다.

  3. 템플릿 목록에서 SQL Server 2008 데이터베이스 프로젝트를 클릭합니다.

  4. 이름에 RefactorAdventureWorks를 입력하고 확인을 클릭합니다.

    테스트(샌드박스라고도 함) 프로젝트로 RefactorAdventureWorks라는 빈 데이터베이스 프로젝트가 포함된 솔루션이 만들어집니다.

    다음에는 배포된 AdventureWorks 데이터베이스의 인스턴스에서 스키마를 가져옵니다.

AdventureWorks 데이터베이스를 가져오려면

  1. 솔루션 탐색기 또는 스키마 뷰에서 RefactorAdventureWorks를 클릭합니다.

  2. 프로젝트 메뉴에서 데이터베이스 개체 및 설정 가져오기를 클릭합니다.

    참고

    RefactorAdventureWorks를 마우스 오른쪽 단추로 클릭하고 데이터베이스 개체 및 설정 가져오기를 클릭할 수도 있습니다.

    데이터베이스 가져오기 마법사가 나타납니다.

  3. 소스 데이터베이스 연결 목록에서 AdventureWorks 데이터베이스에 해당하는 연결을 클릭합니다.

    중요

    해당 데이터베이스에 아직 연결하지 않은 경우 우선 새 연결을 클릭하여 연결을 만들어야 합니다. 자세한 내용은 방법: 데이터베이스 연결 만들기를 참조하십시오.

  4. 시작을 클릭한 다음, 개체 및 설정을 가져왔으면 마침을 클릭합니다.

    스키마를 가져올 때 데이터베이스에 있는 개체에 해당하는 프로젝트 항목이 솔루션 탐색기스키마 뷰의 데이터베이스 프로젝트 아래에 나타납니다.

    참고

    데이터베이스에 연결하여 스키마를 가져왔지만 이제 연결이 끊어지고 오프라인으로 작업합니다.

    다음에는 데이터베이스 개발의 일반적인 작업인 데이터베이스에 코드를 추가하는 작업을 수행합니다.

일반적인 데이터베이스 개발 작업 구현

이 작업에서는 각 직원의 결근 기록을 추적하기 위한 지원 기능을 구현하라는 요청을 받았다고 가정합니다. 이 작업의 일부로 다음 개체를 만들어야 합니다.

  • 각 결근의 시작 및 끝 날짜와 결근 사유(휴가, 병가, 예비군 소집, 연/월차, 무급 휴가 또는 상조 휴가)를 추적하는 테이블. 이 연습의 뒷부분에서 이 테이블을 Person 스키마에 추가합니다. 테이블의 데이터에는 다음과 같은 제한 사항이 있습니다.

    • 결근은 한 번에 5일을 초과할 수 없습니다. 그 이상의 결근은 여러 항목으로 나뉩니다.

    • 결근에는 유효한 날짜 범위가 있습니다.

    • 이 테이블은 EmployeeID를 기준으로 Employee 테이블과 연결됩니다.

  • 각 직원의 전체 결근 기록을 보여 주는 뷰

  • 결근을 기록하고, 결근 사유가 휴가인 경우 직원의 휴가 시간을 업데이트하는 저장 프로시저

코드를 추가할 준비를 하려면

  1. 보기 메뉴에서 데이터베이스 스키마 뷰를 클릭합니다.

  2. 스키마 뷰에서 RefactorAdventureWorks 노드를 확장합니다.

  3. 스키마 뷰가 개체 형식별로 정렬된 경우 도구 모음에서 개체 그룹화 변경을 클릭합니다.

    참고

    스키마 뷰는 '테이블 및 뷰'라는 노드가 포함된 경우 개체 형식별로 정렬됩니다. 스키마 뷰에 '스키마'라는 노드가 포함되어 있으면 다음 절차를 계속할 수 있습니다.

    다음에는 데이터베이스 프로젝트에 AbsenceHistory 테이블을 추가합니다.

AbsenceHistory 테이블을 추가하려면

  1. 스키마 뷰에서 스키마 노드, Person 하위 노드, Tables 하위 노드를 차례로 확장합니다.

  2. 테이블 하위 노드를 마우스 오른쪽 단추로 클릭하고 추가를 가리킨 다음 테이블을 클릭합니다.

    새 항목 추가 대화 상자가 나타납니다.

  3. 이름에 AbsenceHistory를 입력하고 추가를 클릭합니다.

    Transact-SQL 편집기가 열리고 AbsenceHistory 테이블에 대한 정의가 표시됩니다.

  4. Transact-SQL 편집기에서 기존 테이블 정의를 다음 코드로 바꿉니다.

    CREATE TABLE [Person].[AbsenceHistory]
    (
    [EmployeeID] INT NOT NULL, 
    [BeginDate] DateTime NOT NULL,
    [EndDate] DateTime NOT NULL,
    [AbsenceType] NCHAR(1) NOT NULL
    );
    
  5. 파일 메뉴에서 Person.AbsenceHistory.table.sql 저장을 클릭합니다.

    다음에는 AbsenceHistory 테이블에 CHECK 제약 조건을 추가합니다.

테이블에 CHECK 제약 조건을 추가하려면

  1. 스키마 뷰에서 AbsenceHistory 노드를 확장합니다.

  2. 제약 조건 노드를 마우스 오른쪽 단추로 클릭하고 추가를 가리킨 다음 CHECK 제약 조건을 클릭합니다.

    새 항목 추가 대화 상자가 나타납니다.

  3. 이름에 CK_AbsenceHistory_ValidDates를 입력하고 추가를 클릭합니다.

    Transact-SQL 편집기가 열리고 제약 조건에 대한 정의가 표시됩니다.

  4. Transact-SQL 편집기에서 기존 제약 조건 정의를 다음 코드로 바꿉니다.

    ALTER TABLE [Person].[AbsenceHistory]
    ADD CONSTRAINT [CK_AbsenceHistory_ValidDates] 
    CHECK  (EndDate >= BeginDate AND DateDiff(day, EndDate, BeginDate) <= 5)
    go
    EXECUTE sp_addextendedproperty @name = N'MS_Description', 
    @value = 'Check constraint [EndDate]>= [BeginDate]', 
    @level0type = N'SCHEMA', 
    @level0name = N'Person', 
    @level1type = N'TABLE', 
    @level1name = N'AbsenceHistory', 
    @level2type = N'CONSTRAINT', 
    @level2name = N'CK_AbsenceHistory_ValidDates';
    

    이 코드는 끝 날짜가 시작 날짜보다 이후인지와 두 날짜 사이의 차이가 5일을 초과하지 않는지 확인하는 테이블에 대한 제약 조건을 정의합니다.

  5. 파일 메뉴에서 Person.AbsenceHistory.CK_AbsenceHistory_ValidDates.chkconst.sql 저장을 클릭합니다.

    다음에는 AbsenceHistory 테이블에 외래 키를 추가합니다.

외래 키 정의를 추가하려면

  1. 스키마 뷰에서 노드를 마우스 오른쪽 단추로 클릭하고 추가를 가리킨 다음 외래 키를 클릭합니다.

    새 항목 추가 대화 상자가 나타납니다.

  2. 이름에 FK_AbsenceHistory_Employee_EmployeeID를 입력하고 추가를 클릭합니다.

    Transact-SQL 편집기가 열리고 외래 키에 대한 정의가 표시됩니다.

  3. Transact-SQL 편집기에서 기존 외래 키 정의를 다음 코드로 바꿉니다.

    ALTER TABLE [Person].[AbsenceHistory]
    ADD CONSTRAINT [FK_AbsenceHistory_Employee_EmployeeID] 
    FOREIGN KEY ([EmployeeID]) 
    REFERENCES [HumanResources].[Employee] ([BusinessEntityID]) 
    ON DELETE NO ACTION ON UPDATE NO ACTION;
    
    GO
    EXECUTE sp_addextendedproperty @name = N'MS_Description', 
        @value = 'Foreign key constraint referencing Employee.BusinessEntityID.', 
        @level0type = N'SCHEMA', 
        @level0name = N'Person', 
        @level1type = N'TABLE', 
        @level1name = N'AbsenceHistory', 
        @level2type = N'CONSTRAINT', 
        @level2name = N'FK_AbsenceHistory_Employee_EmployeeID';
    

    이 코드는 AbsenceHistory 테이블의 EmployeeID와 [HumanResources].[Employee] 테이블의 BusinessEntityID 간의 외래 키 관계를 정의합니다.

  4. 파일 메뉴에서 Person.AbsenceHistory.FK_AbsenceHistory_Employee_EmployeeID.fkey.sql 저장을 클릭합니다.

    이제 이 테이블은 기존 스키마 대신 HumanResources 스키마에 있어야 합니다. 다음 절차에서는 이 오류를 수정합니다.

코딩 오류 수정

제약 조건과 외래 키를 이미 정의했으므로 테이블과 해당 관련 개체를 다른 스키마로 이동하는 데는 일반적으로 많은 시간이 소요됩니다. 계속하기 전에 데이터베이스 리팩터링을 사용하여 테이블과 해당 관련 개체를 올바른 스키마로 빠르고 쉽게 이동할 수 있습니다.

AbsenceHistory 테이블을 HumanResources 스키마로 이동하려면

  1. 스키마 뷰에서 AbsenceHistory 테이블을 마우스 오른쪽 단추로 클릭하고 리팩터링을 가리킨 다음 스키마로 이동을 클릭합니다.

    스키마로 이동 대화 상자가 나타납니다.

  2. 새 스키마 목록에서 HumanResources를 클릭합니다.

  3. 변경 내용 미리 보기 확인란이 선택되어 있는지 확인하고 확인을 클릭합니다.

    변경 내용 미리 보기 대화 상자가 나타납니다. 여기에서 변경 내용을 데이터베이스 프로젝트에 적용하기 전에 검토할 수 있습니다.

  4. 적용을 클릭합니다.

    리팩터링 변경 내용이 데이터베이스 프로젝트에 적용됩니다. AbsenceHistory 테이블이 모든 관련 개체와 함께 Person 스키마에서 HumanResources 스키마로 이동합니다.

  5. 스키마 뷰에서 HumanResources 스키마 노드, 테이블 노드를 차례로 확장합니다.

    AbsenceHistory 테이블이 올바른 스키마에 나타납니다.

    참고

    개체를 올바른 스키마로 이동할 때 개체가 정의된 파일의 이름은 변경하지 않았습니다. 파일 이름을 업데이트하려면 솔루션 탐색기에서 파일 이름을 바꿔야 합니다.

    다음에는 개발 작업의 나머지 단계를 완료합니다.

개발 작업 완료

테이블의 스키마를 수정했으므로 이제 다음 개체를 만들어야 합니다.

  • 각 직원의 전체 결근 기록을 보여 주는 뷰

  • 결근을 기록하고, 결근 사유가 휴가인 경우 직원의 휴가 시간을 업데이트하는 저장 프로시저

vEmployeeAbsenceHistory 뷰를 추가하려면

  1. 스키마 뷰의 HumanResources 스키마에서 노드를 확장합니다.

  2. 노드를 마우스 오른쪽 단추로 클릭하고 추가를 가리킨 다음 를 클릭합니다.

    새 항목 추가 대화 상자가 나타납니다.

  3. 이름에 vEmployeeAbsenceHistory를 입력하고 추가를 클릭합니다.

    Transact-SQL 편집기가 열리고 뷰에 대한 정의가 표시됩니다.

  4. Transact-SQL 편집기에서 기존 뷰 정의를 다음 코드로 바꿉니다.

    CREATE VIEW [HumanResources].[vEmployeeAbsenceHistory]
    AS 
    SELECT 
        a.* 
        ,c.[Title] 
        ,c.[FirstName] 
        ,c.[MiddleName] 
        ,c.[LastName] 
        ,c.[Suffix] 
    FROM [HumanResources].[Employee] e
        INNER JOIN [Person].[Person] c 
        ON c.[BusinessEntityID] = e.[BusinessEntityID]
        INNER JOIN [AbsenceHistory] a 
        ON e.[BusinessEntityID] = a.[EmployeeID] 
    ;
    
    GO
    EXECUTE sp_addextendedproperty @name = N'MS_Description', 
    @value = 'Returns employee name and absence history.', 
    @level0type = N'SCHEMA', 
    @level0name = N'HumanResources', 
    @level1type = N'VIEW', 
    @level1name = N'vEmployeeAbsenceHistory';
    

    이 코드는 Employee, Contact 및 AbsenceHistory 테이블의 조합에서 데이터를 반환하는 뷰를 정의합니다.

  5. 파일 메뉴에서 HumanResources.vEmployeeAbsenceHistory.view.sql 저장을 클릭합니다.

    다음에는 저장 프로시저를 추가합니다.

uspRecordAbsence 저장 프로시저를 추가하려면

  1. 스키마 뷰의 HumanResources 스키마에서 프로그램 기능 노드, 저장 프로시저 노드를 차례로 확장합니다.

  2. 저장 프로시저 노드를 마우스 오른쪽 단추로 클릭하고 추가를 가리킨 다음 저장 프로시저를 클릭합니다.

    새 항목 추가 대화 상자가 나타납니다.

  3. 이름에 uspRecordAbsence를 입력하고 추가를 클릭합니다.

    Transact-SQL 편집기가 열리고 저장 프로시저에 대한 정의가 표시됩니다.

  4. Transact-SQL 편집기에서 기존 저장 프로시저 정의를 다음 코드로 바꿉니다.

    CREATE PROCEDURE [HumanResources].[uspRecordAbsence]
    @EmployeeID INT,
    @AbsenceType NCHAR(1),
    @StartDate DATETIME,
    @EndDate DATETIME
    AS
    BEGIN
    BEGIN TRANSACTION
    INSERT INTO [AbsenceHistory] (EmployeeID, BeginDate, EndDate, AbsenceType)
    VALUES(@EmployeeID, @StartDate, @EndDate, @AbsenceType)
    IF (@AbsenceType = 'V')
    BEGIN
    UPDATE [Employee]
    SET [VacationHours] = [VacationHours] - DateDiff(day, @StartDate, @EndDate)
    WHERE [BusinessEntityID] = @EmployeeID
    END
    COMMIT TRANSACTION
    END;
    

    이 코드는 AbsenceHistory 테이블에 행을 추가하고 결근 사유가 'V'인 경우 Employee 테이블의 VacationHours 필드를 업데이트하는 저장 프로시저를 정의합니다.

  5. 파일 메뉴에서 dbo.uspRecordAbsence.proc.sql 저장을 클릭합니다.

    다음 절차에서는 코드 검토 과정에서 받은 피드백을 처리합니다.

코드 검토 피드백 처리

이 절차에서는 팀의 다른 멤버와 코드를 검토하여 몇 가지 모범 사례에 대한 피드백을 받았다고 가정합니다. 피드백에 따르면 데이터베이스 코드에 대해 정적 코드 분석을 실행할 경우 SELECT *는 경고를 생성하므로 이를 사용하지 않아야 합니다. 또한 저장 프로시저에 사용한 이름을 정규화해야 합니다. 마지막으로, AbsenceHistory 테이블의 BeginDate 열 이름을 StartDate로 바꿔야 합니다.

참고

코딩 표준 및 요구 사항은 팀에 따라 다를 수 있습니다. Transact-SQL 코드를 작성할 때는 조직의 코딩 표준을 적용해야 합니다. 이 연습에서는 두 가지 문제를 보여 주고 있습니다. 또한 이러한 기술은 일반적으로 단일 데이터베이스 개체에만 적용하는 것이 아니라, 예를 들어 새 코드의 모든 이름을 정규화할 때와 같이 모든 새 코드에 적용하게 됩니다.

이러한 유형의 변경은 구현하기가 번거로우며 오류가 발생하기 쉽습니다. 데이터베이스 리팩터링을 사용하면 데이터베이스 코드, 테스트 코드 및 데이터 생성 계획을 빠르고 쉽게 업데이트할 수 있습니다.

뷰 정의에서 SELECT *를 확장하려면

  1. 스키마 뷰에서 vEmployeeAbsenceHistory 뷰를 두 번 클릭합니다.

    Transact-SQL 편집기가 열리고 뷰에 대한 정의가 표시됩니다.

  2. 데이터 메뉴에서 리팩터링을 가리키고 와일드카드 확장을 클릭합니다.

    변경 내용 미리 보기 대화 상자가 나타납니다.

  3. 와일드카드 확장 목록에서 **a.***를 클릭합니다.

    뷰에 적용될 업데이트 내용이 변경 내용 미리 보기 창에 표시됩니다.

  4. 적용을 클릭합니다.

    변경 내용이 데이터베이스 프로젝트에 적용됩니다. 다음에는 이 연습의 이전 절차에서 정의한 저장 프로시저에서 이름을 정규화합니다.

저장 프로시저에서 이름을 정규화하려면

  1. 스키마 뷰에서 uspRecordAbsence 저장 프로시저를 두 번 클릭합니다.

    Transact-SQL 편집기가 열리고 저장 프로시저에 대한 정의가 표시됩니다.

  2. 데이터 메뉴에서 리팩터링을 가리키고 이름 정규화를 클릭합니다.

    변경 내용 미리 보기 대화 상자가 나타나고 리팩터링 작업을 프로젝트에 적용할 경우의 모든 변경 내용이 표시됩니다.

  3. 변경 내용을 검토한 후 적용을 클릭합니다.

    변경 내용이 데이터베이스 프로젝트에 적용됩니다.

BeginDate 열의 이름을 바꾸려면

  1. 스키마 뷰에서 AbsenceHistory 테이블과 열 노드를 차례로 확장하고 BeginDate 열을 클릭합니다.

  2. 데이터 메뉴에서 리팩터링을 가리킨 다음 이름 바꾸기를 클릭합니다.

    이름 바꾸기 대화 상자가 표시됩니다.

    참고

    스키마 뷰에서 BeginDate를 마우스 오른쪽 단추로 클릭하고 리팩터링을 가리킨 다음 이름 바꾸기를 클릭할 수도 있습니다.

  3. 새 이름에 StartDate를 입력합니다.

  4. 변경 내용 미리 보기 확인란을 선택하고 확인을 클릭합니다.

    변경 내용 미리 보기 대화 상자가 나타나고 이름 바꾸기 작업을 데이터베이스 프로젝트에 적용할 경우의 모든 변경 내용이 표시됩니다.

  5. 적용을 클릭합니다.

    변경 내용이 적용됩니다. 열 이름이 업데이트되고 업데이트된 각 개체에 대해 새 열 이름이 스키마 뷰에 나타납니다. 이 항목의 앞에서 지정한 날짜 제약 조건에 대한 정의를 열면 이 제약 조건도 새 열 이름을 참조하도록 업데이트되어 있습니다.

다음 단계

이 시점에서는 일반적으로 코드 검토를 수행한 팀 멤버와 업데이트를 검토한 다음 변경 내용을 버전 제어에 체크 인합니다. 이때 데이터베이스 스키마의 오프라인 표현인 데이터베이스 프로젝트는 업데이트되어 있습니다. 데이터베이스 프로젝트를 대상 데이터베이스에 배포해야만 배포된 스키마가 업데이트됩니다.

데이터베이스 프로젝트에 리팩터링 작업을 적용할 때, sp_rename 또는 ALTER를 사용하여 개체 이름을 바꾸거나 개체를 이동할 수 있으면 리팩터링 작업에 대한 정보가 리팩터링 로그 파일에 기록됩니다. 이 연습에서 이 로그 파일의 이름은 RefactorAdventureWorks.refactorlog로 지정됩니다. 이 리팩터링 로그 파일은 배포 시 리팩터링 변경 내용의 의도를 보존하기 위한 목적으로 사용됩니다. 예를 들어 사용자가 열 이름을 바꾸면 리팩터링 로그에 해당 변경 내용이 기록됩니다. 배포 시 이 정보는 기존 이름의 열과 이 열에 포함된 데이터가 저장되지 않도록 하고 빈 열이 새 이름으로 만들어지지 않도록 합니다. 리팩터링을 사용할 경우 배포 전 및 배포 후 스크립트에 데이터를 보존하기 위한 문을 추가하지 않아도 됩니다.

참고 항목

작업

방법: 데이터베이스 리팩터링 변경 내용 배포

개념

데이터베이스 개체를 다른 스키마로 이동

데이터베이스 개체 이름 정규화

SELECT 문에서 와일드카드 문자 확장

데이터베이스 코드를 분석하여 코드 품질 향상