2006-07-07

웹 UI 가이드라인

1. 개요

HTML(Hyper Text Markup Language)과 CSS(Cascading Style Sheet)를 사용한 웹 UI 작성 가이드라인을 설명한다. 이 가이드라인은 HTML 코드의 최소화와 간결성 확보를 통한 웹 어플리케이션 개발 생산성과 유지 보수성 향상을 목표로 한다. 그리고 트래픽의 최소화를 통한 웹 어플리케이션 성능 향상도 목표로 한다.

또한 가능한 대중적으로 사용하는 브라우저(MS IE, 모질라 파이어폭스, 오페라, 사파리 등)에서 정상적으로 동작하는 HTML과 CSS를 작성하는 것을 목표로 한다.

이를 위해서 W3C 표준 기술을 중심으로 웹 UI를 작성하는 것을 권장한다. HTML은 XHTML 1.0을 기준으로 하고, CSS는 대중적으로 많이 사용하는 브라우저를 기준으로 한다. 단, 자세한 버전은 프로젝트와 어플리케이션의 상황에 맞게 조정한다.


2. 웹 표준 가이드

웹 표준과 관련해서 명심해야 하는 사항은 모든 브라우저에서 동일하게 보이도록 UI를 구성하는 것이 목표가 아니라는 점이다. 여기에 과도하게 집중하면 효과보다 큰 비용이 발생할 수 있다. 웹 표준를 통한 웹 UI의 작성의 목표는 모든 브라우저에서 사용자가 사용하는데 큰 불편함을 느끼지 않도록 하는 것이다.


3. HTML 작성 가이드라인

일반적인 HTML 작성 가이드라인은 아래와 같다.


(1) XHTML 사용

일반 HTML 4.0 보다 XML 표준을 준수하는 XHTML 1.0을 사용한다. 따라서 모든 HTML 문서는 아래처럼 시작해야 한다.

<?xml version="1.0" encoding="UTF-8"?> <!-- MS IE를 고려하여 삭제 -->

<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko" lang="ko">
</html>

단, XML 선언부가 있는 경우에 MS IE 6.0은 비표준 방식으로 UI를 표현함으로 이를 삭제하도록 한다.


그리고 XHTML 작성에 있어서 XML의 일반적인 작성 원칙을 준수한다.


DOCTYPE을 선언한다. 문서의 앞 부분에 DOCTYPE을 선언한다.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN">


html 태그에 xmlns 속성을 기입한다.

<html xmlns="http://www.w3.org/1999/xhtml"></html>


소문자를 사용한다. XML은 대소문자를 구분하며 XHTML은 태그와 속성의 이름으로 소문자를 사용한다. 그리고 속성의 값도 소문자 사용을 기본으로 한다.

<table border="0">
   
</table>


속성 값은 큰 따옴표(Double Quatation) 안에 넣는다. 큰 따옴표 없이 속성에 대한 값을 지정하지 않는다.

<a href="http://www.doosan.com">Doosan</a>


속성에는 항상 값을 기입한다.

<hr noshade="noshade">


& 속성 값에 대해서 &amp;를 사용한다.

<a href="List.jsp?a=1&amp;b=2">


모든 태그는 닫는다.

<p>모든 태그는 닫는다.</p>

특히 내용이 없는 태그 사용에 주의한다.

<br/>
<input type="text" name="id"/>

이런 태그를 독립형 태그라고 한다.

<area>, <meta/>, <base/>, <col>, <frame>, <link/>, <br/>, <hr/>, <img/>, <input/>, <param/>

다음은 독립형 태그로 혼동하기 쉬운 컨테이너 태그이다.

<script></script>, <object></object>, <div></div>


style과 script 태그의 내용은 모두 주석 처리한다.  style 혹은 script 태그 안에서 XML 태그 요소를 사용하려면 항상 주석 처리한다.

<script><!--

  function doAction() {
    if (a < b) ...
  }

//--></script>


(2) 이미지 사용의 최소화

이미지 사용을 최소화하며 반복적인 이미지를 사용해야 하는 경우에 CSS의 background-image 속성으로 처리한다.

간격 조절을 위한 투명 이미지는 절대로 사용하지 않는다.

이미지의 크기는 이미지 파일의 절대 크기를 그대로 사용한다. 따라서 width와 height를 img 태그에 지정하지 않는다. 그러나 width와 height 속성을 명시적으로 지정하면 웹 브라우저 처리 속도의 향상을 기대할 수 있다.


(3) 구성 요소 간의 간격 조절

HTML 구성 요소 간의 간격 조절에 투명 이미지나 br 태그나 &nbsp;의 사용을 지양하고 CSS로 조정한다.


(4) 태그의 속성 값의 최소화

HTML로는 자료의 내용 및 구조만을 표현하며, UI 표현과 관련한 정보는 CSS로 나타낸다. 따라서 HTML 태그 속성 사용을 최소화한다.


(5) 들여 쓰기 규정

들여 쓰기는 탭이 아닌 스페이스 4자로 한다.


(6) table 태그의 사용

table 태그는 그리드 형 데이터 표현에만 사용한다. table 태그로 레이아웃을 표현하는 것은 지양해야 하며, table 태그 안에 table 태그가 들어가는 형식은 특별한 이유가 아니면 사용하지 않는다.

데이터를 위한 테이블의 경우, row와 column에 대한 헤더(th 태그)를 명기한다.


(7) 리스트 태그를 통한 구조적 데이터 표현

구조적인 데이터는 ul과 li 등의 리스트 태그로 표현한다.


(8) 상대 경로를 사용

모든 URL 경로의 표현은 상대 경로로 한다. 수정을 어렵게 하는 절대 경로를 사용하는 것은 지양한다.


(9) 텍스트를 이미지로 표현하는 것을 지양

텍스트를 이미지 파일로 표현하는 것을 지양한다. 이미지를 사용하면 네트워크 트래픽을 증가시키는 동시에 수정을 어렵게 한다.


(10) Content-based text styles

표현 방식을 지칭하는 b, big, i, small, sub, sup, tt 태그 사용을 지양하고(Physical Styles) 의미 방식을 지칭하는 strong, em 태그의 사용을 지향한다.

  • abbr - abbreviated form of a longer word or phrase
  • acronym - acronym
  • em - 강조해야 하는 내용
  • strong - 매우 강조해야 하는 내용
  • cite - 출처, 인용구
  • code - 프로그래밍 코드의 일부분
  • kbd - 사용자가 입력한 내용
  • samp - 샘플 내용
  • dfn - 정의
  • var - 변수


(11) 사용을 지양해야 하는 태그

center, font, basefont, u, s, strike, marquee 등의 태그 사용을 지양한다.


(12) 사용을 지양해야 하는 속성

태그의 align, style 등의 속성 사용을 지양한다.


(13) img 태그 사용의 지양

이미지를 표현하는데 있어서 img 태그의 사용은 지양하고 CSS의 background-image 속성의 사용을 지향한다.


(14) 개발 시 편집을 위한 태그의 활용

ins, del 등의 태그를 사용하여 개발 시 편집 정보를 기술한다.


4. CSS 작성 가이드라인

일반적인 CSS 작성 가이드라인은 아래와 같다.


(1) 소문자 사용

CSS의 내용은 모두 소문자로 작성한다.


(2) 들여 쓰기 규정

들여 쓰기는 탭이 아닌 스페이스 4자로 한다.


(3) 선택자

선택자는 태그의 이름, 태그의 클래스(class) 속성, 태그의 아이디(id) 속성 순으로 지정한다.
태그의 계층 구조를 사용하면 태그의 이름을 통한 선택자 사용 비율을 늘릴 수 있다.

table tbody tr td {
    background-color: #ffffff;
    font: 1em 굴림, Tahoma, sans-serif;
    height: 2em;
    padding: 0.5em 0.5em 0 1em;
    overflow: hidden;
}

클래스 속성이나 아이디 속성을 사용하는 경우에도 앞에 태그를 지정한다.

div.title-area {
    ...
}

div#header {
    ...
}


(4) 기본 설정의 초기화

html, body, form 태그 등의 기본 설정이 브라우저에 따라서 상이하다. 따라서 이를 CSS를 통해서 동일하게 설정한다.

html, body, form {
    margin: 0;
    padding: 0;
}


(5) 상속을 통한 속성 반복의 최소화

상속을 통하여 동일한 속성 값을 반복하는 것을 지양한다.

div {
    width: 775em;
}

div.title-area {
    color: #0b9af3;
    margin: 0 0 1em 0;
    padding-left: 2em;
}


(6) em을 이용한 크기 설정

해당 요소의 크기를 조정하기 위해 사용하는 여러 단위 중에서 em을 사용하여 크기를 조정한다. 단 border, margin, padding 등과 같은 경우에는 px를 사용할 수 있다.


(7) 색상 속성

색상(color) 속성에 대한 값은 색상 이름이 아닌, 헥사 코드로 표현한다.

bgcolor: #ffffff;

rgb로 표현할 수도 있다.

bgcolor: rgb(255, 255, 255);


(8) background-image 속성을 통해서 이미지 설정

background-image 속성을 통해서 HTML에서의 이미지(img) 태그 사용을 최소화한다.

div.title-area {
    background-image: url(../image/label/Title2.gif);
    background-position: 0 -2px;
    background-repeat: no-repeat;
    margin: 0 0 1em 0;
    padding-left: 2em;
}


(9) 집합 속성의 사용을 지향

CSS의 내용을 간결하게 가기 위해서 집합 속성을 사용한다. 아래는 개별 속성을 사용한 경우이다.

div.title-area {
    margin-top: 5px;
    margin-right: 4px;
    margin-bottom: 3px;
    margin-left: 2px;
}

위의 표현을 아래처럼 집합 속성으로 표현한다.

div.title-area {
    margin: 5px 4px 3px 2px;
}


(10) 웹 브라우저 독립적인 속성의 사용

특정 웹 브라우저에 종속적인 속성의 사용을 지양하며, W3C가 제공하는 아래의 속성 테이블을 중심으로 CSS 파일을 작성한다.

Cascading Style Sheets, level 2 CSS2 Specification, Appendix F. Property index (http://www.w3.org/TR/REC-CSS2/propidx.html)


(11) CSS 파일 수의 최소화

CSS 파일의 수를 최소화한다. 유사한 형태의 업무 화면은 하나의 CSS 파일로 조정한다. 각 업무 화면에 특징적인 요소를 별개의 CSS 파일로 지정하기 보다는 HTML의 style 태그를 이용해서 설정하는 것을 권장한다.

단 메인 화면, 사이트 맵 등 업무 화면과는 독립적인 화면에 대해서는 별도의 CSS 파일을 가져간다.


(12) 특기 사항

CSS와 관련한 특기 사항은 아래와 같다.

  • select 태그 CSS 적용 - border 속성을 지정할 수 없음


5. 참고 자료



2006-05-21

NTLM 인증

윈도우 인트라넷 환경에서 NTLM(Windows NT LAN Manager) 프로토콜로 SSO(Single Sign On)를 구현하는 경우가 있다.

자바(JSP)에서 NTLM 프로토콜을 이용해서 윈도우 도메인 이름과 사용자 계정을 획득하는 방법이다.

<%@ page contentType="text/html; charset=UTF-8" %>

<%
String auth = request.getHeader("Authorization");
if (auth == null) {
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    response.setHeader("WWW-Authenticate", "NTLM");
    response.flushBuffer();
    return;
    // 이 응답을 받은 IE 웹 브라우저는 다시 요청을 보낸다.
}

if (auth.startsWith("NTLM ")) {
    byte[] msg = new sun.misc.BASE64Decoder().decodeBuffer(auth.substring(5));
    int off = 0, length, offset;
    if (msg[8] == 1) {
        byte z = 0;
        byte[] msg1 = { (byte) 'N', (byte) 'T', (byte) 'L',
                        (byte) 'M', (byte) 'S', (byte) 'S', (byte) 'P', z,
                        (byte) 2, z, z, z, z, z, z, z, (byte) 40, z, z, z,
                        (byte) 1, (byte) 130, z, z, z, (byte) 2, (byte) 2,
                        (byte) 2, z, z, z, z, // this line is 'nonce'
                        z, z, z, z, z, z, z, z };
        response.setContentLength(0);
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        String value = "NTLM " + new sun.misc.BASE64Encoder().encodeBuffer(msg1);

        // 웹로직에서는 Header 값에 CRLF 문자를 포함하면 예외가 발생함.
        response.setHeader("WWW-Authenticate", value.replaceAll("\n", ""));
        response.flushBuffer();
        return;
        // 이 응답을 받은 IE 웹 브라우저는 다시 요청을 보낸다.
    } else if (msg[8] == 3) {
        // 최종적으로 윈도우 도메인 이름과 사용자 계정을 도출할 수 있는 요청이다.
        String remoteHost = null;
        String domain = null;
        String username = null;

        off = 30;
        length = msg[off + 17] * 256 + msg[off + 16];
        offset = msg[off + 19] * 256 + msg[off + 18];
        remoteHost = new String(msg, offset, length);

        length = msg[off + 1] * 256 + msg[off];
        offset = msg[off + 3] * 256 + msg[off + 2];
        domain = new String(msg, offset, length);

        length = msg[off + 9] * 256 + msg[off + 8];
        offset = msg[off + 11] * 256 + msg[off + 10];
        username = new String(msg, offset, length);

        // 사용자 계정에 포함된 특수 문자 제거
        username = username.replaceAll("\\W", "");
        return;
    }
}
%>


물론 이 경우에는 MS IE 웹 브라우저만을 사용해야 한다.


참고 자료



2006-05-05

Active Directory 연동

LDAP(Lightweight Directory Access Protocal)과 JNDI(Java Naming and Directory Interface) API를 이용해서 MS Active Directory와 연동(사용자 정보 검색)하는 방법이다.


Active Directory 서버 연결 시작

ActiveDirectoryDao 클래스의 생성자에서 Active Directory 서버와 연결한다.

ActiveDirectoryDao.java

private DirContext ldapContext;

private String host = "127.0.0.1";

private String port = "389";

private String username = "username";

private String password = "password";

public ActiveDirectoryDao() {
    try {
        Hashtable ldapEnv = new Hashtable(5);
        ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
        ldapEnv.put(Context.PROVIDER_URL, "ldap://" + host + ":" + port);
        ldapEnv.put(Context.SECURITY_PRINCIPAL, username);
        ldapEnv.put(Context.SECURITY_CREDENTIALS, password);
        ldapContext = new InitialDirContext(ldapEnv);
    } catch (NamingException e) {
        throw new SystemException(e);
    }
}


Active Directory 서버 연결 종료

ActiveDirectoryDao 클래스의 close 메소드로 Active Directory 서버와의 연결을 종료한다.

ActiveDirectoryDao.java

public void close() {
   if (ldapContext != null) {
       try {
           ldapContext.close();
       } catch (NamingException e) {
           throw new SystemException(e);
       }
   }
}


사용자 존재 여부 확인

ActiveDirectoryDao 클래스의 hasUser 메소드로 Active Directory 서버에 특정 사용자 정보가 존재하는지 확인한다.

ActiveDirectoryDao.java

private String baseDn = "...";

public boolean hasUser(String name) {
    try {
        NamingEnumeration all = ldapContext.search(baseDn, "name=" + name,
                getSearchControl());
        return all.hasMoreElements();
    } catch (NamingException e) {
        throw new SystemException(e);
    }
}


하위 노드에서도 사용자 정보를 검색하도록 SearchControls를 설정한다. 사용자 아이디가 유일해야 한다.

ActiveDirectoryDao.java

protected SearchControls getSearchControl() {
    SearchControls result = new SearchControls();
    result.setCountLimit(1);
    result.setSearchScope(SearchControls.SUBTREE_SCOPE);
    return result;
}


사용자 정보 확인

ActiveDirectoryDao 클래스의 getUser 메소드로 Active Directory 서버에 있는 특정 사용자 정보를 java.util.Map 객체로 반환한다. 사용자가 존재하지 않으면 java.util.Collections.EMPTY_MAP 객체를 반환한다.


ActiveDirectoryDao.java

public Map getUser(String name) {
    try {
        NamingEnumeration all = ldapContext.search(baseDn, "name=" + name,
                getSearchControl());
        while (all.hasMoreElements()) {
            SearchResult each = (SearchResult) all.nextElement();
            Map result = new HashMap();
            NamingEnumeration attributes = each.getAttributes().getAll();
            while (attributes.hasMoreElements()) {
                Attribute attribute = (Attribute) attributes.nextElement();
                result.put(attribute.getID(), attribute.get());
            }
            return result;
        }
        return Collections.EMPTY_MAP;
    } catch (NamingException e) {
        throw new SystemException(e);
    }
}


사용 예제

아래처럼 ActiveDirectoryDao 클래스를 사용한다.

Client.java

ActiveDirectoryDao dao = null;

try {
    dao = new ActiveDirectoryDao();
    Map userInfo = null;
    if (dao.hasUser("username")) {
        userInfo = dao.getUser("username");
    }
} finally {
    if (dao != null) {
        dao.close();
    }
}

Active Directory 서버와의 연결을 유지하려면 close 메소드를 호출하지 않는다.


참고 자료



* ActiveDirectoryDao 전체 소스 코드

package com.dimdol.example;

import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

/**
 * JNDI/LDAP을 이용해서 MS Active Directory와 연동하는 클래스.
 *
 * @version 1.0
 */
public class ActiveDirectoryDao {

    /**
     * 기본 루트.
     */
    private DirContext ldapContext;

    /**
     * Active Directory 서버의 호스트.
     */
    private String host = "active.dimdol.com";

    /**
     * Active Directory 서버의 포트.
     */
    private String port = "389";

    /**
     * 사용자.
     */
    private String username = "";

    /**
     * 패스워드.
     */
    private String password = "";

    /**
     * 기본 Distinguished Name,
     */
    private String baseDn = "OU=조직이름,DC=corp,DC=dimdol,DC=com";

    /**
     * 기본 생성자. Active Directory 서버와 연결한다.
     */   
    public ActiveDirectoryDao(String username, String password) {
        try {
            Hashtable ldapEnv = new Hashtable(5);
            ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY,
                    "com.sun.jndi.ldap.LdapCtxFactory");
            ldapEnv.put(Context.PROVIDER_URL, "ldap://" + host + ":" + port);
            ldapEnv.put(Context.SECURITY_PRINCIPAL, username + "@corp.dimdol.com");
            ldapEnv.put(Context.SECURITY_CREDENTIALS, password);
            ldapContext = new InitialDirContext(ldapEnv);
        } catch (NamingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Active Directory 서버와의 연결을 종료한다.
     */
    public void close() {
        if (ldapContext != null) {
            try {
                ldapContext.close();
            } catch (NamingException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 사용자가 존재하는지 확인한다.
     *
     * @param name
     *            사용자 아이디
     * @return 사용자가 존재하면 true를, 존재하지 않으면 false를 반환한다.
     */
    public boolean hasUser(String name) {
        try {
            NamingEnumeration all = ldapContext.search(baseDn, "name=" + name,
                    getSearchControl());
            return all.hasMoreElements();
        } catch (NamingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 사용자 정보를 java.util.Map 객체에 담아서 반환한다. 사용자가 존재하지 않으면
     * java.util.Collections.EMPTY_MAP 객체를 반환한다.
     *
     * @param name
     *            사용자 아이디
     * @return 사용자 정보를 담고 있는 java.util.Map 객체
     */
    public Map getUser(String name) {
        try {
            NamingEnumeration all = ldapContext.search(baseDn, "name=" + name,
                    getSearchControl());
            while (all.hasMoreElements()) {
                SearchResult each = (SearchResult) all.nextElement();
                Map result = new HashMap();
                NamingEnumeration attributes = each.getAttributes().getAll();
                while (attributes.hasMoreElements()) {
                    Attribute attribute = (Attribute) attributes.nextElement();
                    result.put(attribute.getID(), attribute.get());
                }
                return result;
            }
            return Collections.EMPTY_MAP;
        } catch (NamingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 하위 노드에서도 검색하도록 설정한 SearchControls를 반환한다.
     *
     * @return 하위 노드에서도 검색하도록 설정한 SearchControls
     */
    protected SearchControls getSearchControl() {
        SearchControls result = new SearchControls();
        result.setCountLimit(1);
        result.setSearchScope(SearchControls.SUBTREE_SCOPE);
        return result;
    }

    public static void main(String[] args) throws Exception {
        ActiveDirectoryDao dao = new ActiveDirectoryDao("","");
        System.out.println(dao.hasUser("dimdol"));
        System.out.println(dao.getUser("dimdol").get("description"));
        dao.close();
    }

}


2006-04-21

CVSNT 설치와 관리

1. 개요

형상관리 도구인 CVSNT를 설치하고 사용하는 방법을 설명한다. 여기서 설명하는 CVSNT의 버전은 2.5.04.3510이고, 여기에서 다운로드한다.

사용자 관리 등의 클라이언트 용도로 CVSNT를 설치한다면 <2. 설치>와 <5. 사용자 관리>만을 참고한다. 그리고 CVSNT를 설치하면 자동으로 윈도우 서비스에 등록되기 때문에 클라이언트 목적으로 설치하였다면 서비스를 중지시키고 상태를 자동에서 수동으로 수정한다.

2. 설치

(1) 설치와 관련한 주의 사항

안정성 등의 이유로 CVSNT를 NTFS 파일 시스템에 설치하는 것을 권장하지만 FAT 파일 시스템에 설치해도 된다.

설치하기 전에 기존에 설치한 CVSNT와 CVS 프로그램을 삭제하는 것을 권장한다. 삭제하기 곤란한 경우에는 최소한 PATH가 겹치는 일은 방지해야 한다. 예를 들어 WinCVS의 cvs.exe나 Cygwin의 cvs.exe 등의 프로그램은 CVSNT 일부 기능의 수행을 방해할 수 있다.

윈도우 2003에 CVSNT를 설치하는 경우 WMI 서비스와 연관되어 문제가 발생하는 경우가 있다. 이 경우 WMI 서비스를 중단하고 CVSNT를 설치한 뒤, WMI 서비스를 재시작한다. 이는 CVSNT 설치시에만 해당하며 이 두 서비스를 동시에 운영하는 것에는 문제가 되지 않는다.


(2)  CVSNT 설치 과정

다운로드한 cvsnt-server-2.5.04.3510.msi 파일을 클릭하여 설치를 시작한다. Next 버튼을 클릭한다.

Setup 화면


라이센스에 동의한 후에 Next 버튼을 클릭한다.

End-User License Agreement 화면


Typical을 선택한 후 Next 버튼을 클릭한다.

Choose Setup Type 화면


Install 버튼을 클릭하면 프로그램 복사를 시작한다.

Ready to Install 화면


아래는 복사가 진행 중인 화면이다.

Installing CVSNT 화면


설치가 완료되면 Finish 버튼을 클릭한다.

설치 완료 화면


설치를 마친 후에는 운도우를 다시 시작해야 한다. CVSNT는 윈도우 서비스에 자동으로 등록되어 윈도우가 시작될 때 함께 시작한다. CVSNT를 서버가 아닌 클라이언트로만 사용한다면 서비스를 중지시키고 시작 유형을 자동에서 수동으로 수정한다.

재시작 확인 화면



3. CVSNT 서버의 시작과 종료

윈도우 시작 메뉴의 <CVSNT | CVSNT Control Panel> 메뉴를 통해서 CVSNT 서버를 제어한다. Control Panel의 About 탭 하단의 Services 영역을 통해서 CVSNT 서버를 시작하고 종료한다.

그림 8. About 탭 화면



4. 환경 설정

(1) Repository 관리

Control Panel의 Repository Configuration 탭을 이용해서 Repository를 추가하고 삭제한다. Repository를 추가하려면 하단의 Add 버튼을 클릭한다.

Repository configuration 탭 화면


 
Add 버튼을 클릭하면 나오는 화면의 Location 필드에 오른쪽 버튼을 이용해서 C:/project-temp/repository를 입력한다. Name 필드에는 자동으로 /project-temp/repository가 입력된다. 이 이름이 곧 Repository 경로이다.
 

Server Settings 화면


경로는 상황에 맞게 수정한다.

OK 버튼을 클릭하면 아래처럼 해당 디렉토리를 CVS Repository 디렉토리로 초기화할지에 대해서 물어본다. 예 버튼을 클릭한다.
 

Repository 디렉토리 초기화 화면


권한과 신뢰성 문제 등을 이유로 Repository 디렉토리로 네트워크 드라이브를 선택해서는 안된다.
새로운 Repository를 추가하였다면 Control Panel의 About 탭 하단의 Services 영역을 통해서 CVSNT 서버를 다시 시작한다.

경험적으로 설치 이후나 Repository 추가 이후에 윈도우 혹은 CVSNT를 재시작하지 않아도 정상적으로 동작하는 경우도 있고 그렇지 않은 경우도 있다. 따라서 가급적 재시작하도록 한다.


(2) CVS와의 호환성 유지

CVSNT 기본 설정은 표준 CVS와의 호환에 문제가 있다. 이 문제를 해결하기 위해서 아래처럼 설정한다.
 

Compatibility Options 탭 화면


Control Panel의 Compatibility Options 탭의 Non-CVSNT Clients 영역에서 Respond as cvs 1.11.2 to version request와 Emulate ‘-n checkout’ bug 옵션을 체크한다.


5. 사용자 관리

CVSNT 서버 접근에 사용하는 프로토콜에 따라서 사용자, 그룹, 권한을 다양하게 설정할 수 있다. 다양한 클라이언트와의 호환성과 편리한 설정 등을 이유로 가장 많이 사용하는 프로토콜은 pserver이다. 이 글에서는 pserver 프로토콜에 대한 설정만을 설명한다. 보안상의 이슈가 있다면 ssh(ext) 프로토콜을 사용한다.

pserver 프로토콜을 이용하면 CVSNT가 설치된 윈도우 사용자 계정으로 CVSNT 서버에 접근할 수 있다. 그러나 모든 사용자를 윈도우 사용자 계정으로 등록할 필요는 없다. CVSNT 서버를 관리하기 위한 대표 윈도우 사용자 계정 하나를 두고 이 계정을 이용하여 다른 사용자를 추가할 수 있는데 이렇게 추가한 사용자를 alias라고 한다. 사용자 alias 정보는 Repository 디렉토리 밑의 CVSROOT/passwd 파일에 저장된다.

이 때 사용하는 윈도우 사용자 계정은 관리자(Administrators) 권한을 지녀야 한다.


(1) 사용자 추가

사용자를 추가하기 위해서는 우선 윈도우 사용자 계정으로 CVSNT 서버에 로그인해야 한다. 여기서는 윈도우 사용자 계정 이름을 Administrator로 가정한다. localhost는 CVSNT 서버가 설치된 컴퓨터의 호스트 이름 혹은 IP 주소로 수정한다.

set cvsroot=:pserver:Administrator@localhost:/project-temp/repository
cvs login

Administrator 계정에 대한 패스워드를 입력하면 로그인에 성공한다. 사용자(alias)는 아래처럼 추가한다.

cvs passwd -a -r Administrator 정대만

위의 예처럼 한글 이름으로도 사용자(alias) 계정을 만들 수 있기 때문에 프로젝트에 참여하는 개발자들의 이름으로 사용자(alias)를 추가하는 것을 권장한다. 단, 일부 CVS 클라이언트에서는 사용자 계정이 한글인 경우에 올바르게 동작하지 않는다.

기본적으로 패스워드는 1111로 설정하고, 개별 사용자가 직접 패스워드를 수정하게 한다.

작업이 완료되었으면 아래처럼 로그아웃한다.

cvs logout


(2) 패스워드 수정

수정할 사용자(alias)로 CVSNT 서버에 로그인한다. localhost는 CVSNT 서버가 설치된 컴퓨터의 호스트 이름 혹은 IP 주소로 수정한다.

set cvsroot=:pserver:정대만@localhost:/project-temp/repository
cvs login

정대만 계정에 대한 패스워드를 입력하면 로그인에 성공한다. 패스워드는 아래처럼 수정한다.

cvs passwd

수정할 새로운 패스워드를 입력한다.


(3) 사용자 삭제

사용자를 삭제하기 위해서는 우선 윈도우 사용자 계정으로 CVSNT 서버에 로그인해야 한다. 여기서는 윈도우 사용자 계정 이름을 Administrator로 가정한다. localhost는 CVSNT 서버가 설치된 컴퓨터의 호스트 이름 혹은 IP 주소로 수정한다.

set cvsroot=:pserver:Administrator@localhost:/project-temp/repository
cvs login

Administrator 계정에 대한 패스워드를 입력하면 로그인에 성공한다. 사용자(alias)는 아래처럼 삭제한다.

cvs passwd -X -R 정대만

X와 R은 대문자이다.