새로 ASP.NET 사이트를 구축한다면 반드시 하는 것들 ASP.NET

내가 만약 ASP.NET 사이트를 지금 시점에서 새로 구축한다면, 반드시 다음의 사항들을 반영할 것 같다.

프레임웍

1. ASP.NET 4 기반
웹폼, 그리고 엔티티 프레임웍 기반 작업을 할 때 불편했던 많은 사항들이 개선되었고,
MVC, AJAX 등 여러 프레임웍이 더 이상 설치 필요하지 않게 통합되어있다.
그동안 직접 만들어 써야 했던 여러 것들도 기본 지원한다. 이를테면,
string.IsNullOrWhiteSpace() 같은 함수나 웹폼 컨트롤의 Static ID 지정 등.
충분한 검증이 더 필요한 초대형 사이트가 아니라면 반드시 최신의 프레임웍인 4 기반으로 작업해야 한다.

2. Session State : StateServer 혹은 SQLServer
ASP.NET 어플리케이션의 기본 세션 상태 모드는 "InProc"이다.
이 경우 웹 사이트의 CS 파일을 고치면 IIS가 어플리케이션을 재시작하면서 세션 데이터를 모두 잃게 되어,
접속중인 사용자들의 데이터를 잃거나 하는 여러 치명적 문제가 발생하게 된다.
StateServer와 SQLServer은 이것을 해결하기 위해 선택할 수 있는 두 옵션이다.
나는 StateSever를 구동하는 쪽을 선호한다.

3. Forms Authentication
거의 모든 (해외) ASP.NET에서 책에서 설명하지만, 국내에서는 의외로 잘 쓰지 않는 듯한 Forms Authentication.
ASP.NET 에서 기본 제공하는 다른 인증 방식인 Membership 이
너무 많은 기능으로 인해 정작 커스터마이징이 까다로워 외면받는 것과는 반대로,
가장 기본적인, 특정 사용자의 이름 권한을 세션에 저장하고, 이를 확인하는 기능만을 제공한다.
이 기능을 사용하면 Web.config 파일에서의 설정 및 기본 함수들이 제공되므로,
사이트의 권한 관리 로직을 보다 일관성 있게 유지할 수 있고,
domain 설정을 통해 서브 도메인과의 SSO 구현을 쉽게 할 수 있을 뿐만 아니라,
직접 쿠키를 구현하는 것 보다 보안 측면에서 훨씬 유리하고,
권한이 없는 경우 특정 페이지로 이동시키거나 하는 기능들도 제공되며,
덤으로 클래식 ASP에서 이전중인 사이트의 경우
Request.ServerVariables("AUTH_USER") 변수를 통해 사용자 인증을 통합할 수 있다.

4. Health Monitoring
구동중인 사이트에서 처리되지 않은 런타임 에러가 날 경우
사용자가 보고해주지 않으면 알아채기 힘들고, 그래서 이벤트를 뒤지곤 한다.
ASP.NET 에서는 Health Monitoring API를 사용하면 된다.
단지 Web.config에 몇 가지 항목을 넣는 것만으로, 런타임 에러가 발생하면 즉시 관리자에게 메일을 보내거나 주어진 동작을 하도록 할 수 있다.
기 구동중인 사이트에 이 기능을 구현하게 되면, 갑자기 쏟아지는 많은 메일로 인해 놀랄 수도 있다.

5. Custom Configuration Section / Class
Web.config 파일에 지정할 수 있는 기본 AppSettings 섹션의 문제는 항목이 많아질 경우 관리가 힘들다는 것이다.
모든 항목이 동일 계층에 속하기 때문에 언더스코어(_)와 개행문자로 구분을 해야 한다.
Custom Configuration Section을 정의하면 사이트 설정을 보다 명확하게 정렬할 수 있고,
관련 Class 를 생성하면 코딩시 타이핑 양과 오타로 인해 발생할 수 있는 런타임 에러를 컴파일 타임에 방지할 수 있다.
추가로 도메인이나 DB 계정등 항목에 대해서는 테스트 서버와 실서버용을 구분하여 설정할 수 있도록 해 둔다.

6. Cache
여러 종류의 캐시가 있지만 여기서 말하는 것은 응용프로그램 캐시(HttpContext.Cache)이다.
모든 페이지에서 공지사항이나 특정 아이템들 등 동일 내용을
페이지 어딘가에 반복적으로 보여주기 좋아하는 국내 사이트들의 경우
CRUD 중 Read시 캐시를 이용, 캐싱된 내용이 없으면 새로 읽어들이도록 하고,
나머지 CUD 시에는 캐시를 무효화하도록 하여 DBMS와의 통신량 및 부하를 줄일 수 있다.
Data Access Layer가 확실하게 분리되어 있으며 쿼리가 느린 경우
단 수십 줄의 코딩만으로 확실한 성능 향상을 가져올 수 있는 방법이다.

7. WWW 도메인의 사용 여부 결정
naver.com과 www.naver.com은 다른 도메인이라고 생각해야 한다.
두 도메인에서 같이 서비스할 경우 쿠키 혹은 SSL 사용시(멀티도메인용 인증서가 아닌 경우) 문제가 발생할 수 있다.

8. WebForm 대신 MVC 프레임웍을 사용
일장일단이 있다.
내 생각에 WebForm은 관리자 페이지 등 CRUD를 수행하는 매우 단순한 웹어플리케이션을 만들 때는 좋으나,
자바스크립트를 통해 화면의 요소를 조금이라도 변경하고 제어하거나 서로 다른 컨트롤을 동적으로 제어해야 할 경우
웹폼의 동작 및 검증 방식을 잘 알고 있어야 하며, 그럼에도 전반적으로 움직임을 예측하고 수정하는 것이 매우 복잡해진다.
반면 MVC의 경우 일반 DHTML 코딩과 동일한 방식으로 조작할 수 있기 때문에,
세세한 HTML 조작을 필요로 한다면 MVC로 가야 삽질을 덜 수 있다.
URL 라우팅은 덤이다.

9. Web site project 보다는 Web application project를
웹 사이트 프로젝트를 업로드하면 빌드로 인한 시간이 많이 소요된다.
그리고 빌드에서 검출 가능한 것을 검증하지 않고 업로드하는 경우가 생긴다.
Web.config 파일에서 테스트/실적용 시에 사용할 내용을
Web.Debug.config과 Web.Release.config에 나누어 사용할 수 있다.

10. web.config에 globalization 지정

11. 다음 메타태그들은 검색 엔진에 도움을 주므로 이용한다.
<meta name="description" content="" />
<meta name="keywords" content="" />
<meta name="Author" content="" />
<meta name="Title" content="" />
<meta name="Subject" content="" />

12. favicon 및 아이폰 즐겨찾기를 위한 아이콘 제작

13. robots.txt 제작
그러나 링크가 존재하지 않는 관리자 페이지를 불필요하게 기입하는 것은 오히려 노출시키는 쪽.

보안

1. FTP를 사용할 일이 있을 경우 SFTP 만을 사용한다.
의도치 않은 공유기 혹은 공공 AP 사용시에 계정 정보 및 각종 파일이 노출될 가능성을 줄여 준다.

2. 데이터베이스에 접속 및 권한은 sa 계정이 아닌 별도의 계정을 사용한다.
이렇게 해야 사이트의 Connection String이 노출되거나 인젝션으로 인해 잘못된 명령이 실행되도 시스템 수준의 스크립트가 실행되는 것을 막아준다.

3. MS-SQL의 기본 포트를 변경한다.
이렇게 하지 않으면 산더미같은 접속 시도 로그를 이벤트 뷰어에서 보게 될 것이다.

4. 모든 종류의 비밀번호는 단방향 암호화
저장 및 대조시 FormsAuthentication.HashPasswordForStoringInConfigFile("", "SHA1") 을 쓰면 된다.
변환의 번거로움을 덜기 위해 처음부터 적용해 두자.

5. SSL
개발이 완료된 후 SSL을 적용하는 경우를 종종 보는데, 이 경우
http => https => http 로 이동하는 과정에서 예기치 못한 문제들을 많이 마주하게 된다.
(특히 PostBack을 사용하는 ASP.NET 웹폼 개발의 경우)
커스텀 인증서를 만들어서 개발 과정부터 확실하게 적용해야 한다.

6. 로그인 여부는 OnPreInit 에서 검출
로그인 체크를 Page_Load에서 하는 경우가 많은데, 이 경우 쓸데없이 페이지를 렌더링하는 문제가 생긴다.
페이지 실행을 막으려면 OnPreInit에서 검출 후 리디렉션 시키는 것이 바람직하다.

코딩

1. SqlConnection 클래스 사용시는 반드시 using 키워드를 쓴다. 클래스 멤버 변수로 사용시는 반드시 IDisposable을 통해 Close() 호출을 구현하도록 한다.
SqlCommand, SqlDataReader, SqlDataAdapter도 마찬가지.

2. SqlDataReader 사용시
if(reader.HasRows){
if(reader.Read()){
}
}else{
}

형태로 사용하지 않고

if(reader.Read()){
}else{
}

형태로 일원화한다.

while(reader.Read()){
}

의 경우는 예외.
3. 쿼리 사용시는
string sql = string.Empty;
sql += "SELECT idx, title"
sql += "FROM blah";

형태 대신
string sql = @"
SELECT idx, title
FROM blah";
형태를 사용한다.

where 절 조합이 많은 경우 StringBuilder를 사용한다.

4. Member m = new Member(){
Name = "Hong Gil Dong",
Type = MemberType.Admin
};
보다는
Member m = new Member();
m.Name = "Hong Gil Dong";
m.Type = MemberType.Admin;

을 사용한다.
디버깅 때문이다. 위에서 {} 안에 코드를 넣을 경우 정확하게 에러가 발생한 줄이 표시되지 않는다.

계속 추가 예정.

5. SqlCommand 사용시 혹은 DB 제어시
Update/Delete 문에서는 반드시 affectedRows를 반환하여 처리토록 한다.

int affectedRows = cmd.ExecuteNonQuery();
return affectedRows;

기타

용어의 통일.
회원 : User / Member ?
비회원 : Anonymous / Guest ?

이미지나 파일 저장시
file.jpg
file(1).jpg
식으로 번호를 붙이는 저장은 동일 파일명이 많을수록 파일 시스템을 많이 조회해야 한다는 단점이 있으므로, (file(1000).jpg 까지 있을 경우 파일 시스템을 1000번 조회)
DB 인덱스를 이용한 파일명 혹은 GUID를 이용한 파일명을 사용한다.

DB
반드시 메모리 최대 사용량을 제한한다.
SQL 2008의 경우 서버 속성 - 메모리 에서 설정할 수 있다.

Leave Comments