[C#] 로그인 폼 만들기

사용환경

  • 운영체제 : Microsoft Windows 7 Ultimate K SP1 (64bit)
  • 개발환경 : Microsoft Visual Studio 2010 Professional
  • 개발언어 : C# with WinForm

01. 시작하기 전에…

메인OS를 리눅스를 사용하다보니… 멀티OS를 지원하는 언어를 선택해서 그것을 공부하려고 했다. 근데 업무적으로 Java외의 나머지 영역(메인시스템 외의 나머지 서브시스템 대부분…)은 C#으로 통일하고 있다보니 C#을 보다 깊게 공부해야할 필요를 느꼈다. 사실 워낙 C#이 사용하고 싶고… MSDN 온라인을 조회하면 샘플코드나 참고할 레퍼런스도 많은 편이라서.. 굳이 필요하기 전에는 공부할 필요를 느끼지 못하고 있었다.

그렇지만, 최근에 사내의 C# 소스를 볼 일이 있었는데… 나름(=무진장) 어렵더라 ㅋㅋ 그것을 분석 & 수정 테스트를 하면서 공부를 해볼까 했지만… 개인적인 주제를 가지고서 하나의 애플리케이션을 처음부터 끝까지 개발해보는 것이 어떨까…하고서 개인프로젝트를 시작했다.(사실 약간 지지부진하긴 하다.) 네이버 개발자센터에서 지원하는 SVN을 개설해서 Private로 SVN도 셋팅했으니… 이제 슬슬 발동걸려고 한다.

내 경우에는 전제 소스를 가지고서 작업하는 것을 그다지 좋아하지 않는다. 특히 이전처럼 SVN과 같은 버전관리툴을 사용하지 않은 경우에는 특정 위치로 소스를 Rollback도 어려울 뿐더러… 백업을 잘 못하게 되면 작업된 소스를 잃어버리는 경우도 부지기수였기 때문이다. 아니면 어떤 부분을 수정했는지 모르겠는데 동작하지 않는 경우도 난감하긴 마찬가지이다.

그래서 습관적으로 전체 설계에서 일부분씩 떼어서 분할하여 정복하는(Divide & Conquer) 방식의 개발을 선호한다. 아직 저렙개발자이지만 내 기준으로는 이런 방식의 개발의 장점은 전체의 관점에서는 놓칠 수 있는 세세한 부분까지 신경써서 만들 수 있고, 이렇게 분리할 수 있는 모듈 간의 결합도(Coupling)를 낮출 수 있는 장점도 있다고 생각한다.

반대로 단점은 이렇게 분리하여 개발한 모듈을 합칠 때 전체의 관점을 생각안하고 개발했다가 결합이 될 수 없는 형태로 개발되거나 여러 이유로 분리개발한 모듈을 아예 버리는 상황이 생길 때이다.(완전 허탈하다…) 서론에서 잡설이 너무 긴 경항이 있어서… 이만 줄이고 로그인 폼을 만들기 위한 포스트를 계속하도록 하겠다.

02. 요구사항

간단하게 내가 원하는 로그인 폼에 대해서 몇 가지를 정리해보도록 하겠다.

  1. 메인폼이 뜨기 전에 로그인 폼이 실행된다.
  2. 로그인폼에서 ID와 Password를 입력한다.
  3. 로그인폼에서 취소(Cancel)를 하면, 메인폼도 종료한다.
  4. 로그인폼에서 입력한 내용은 메인폼에 전달한다.

핵심적인 요구사항은 위와 같다. 세부적인 요구사항들은 포스팅을 진행하면서 추가로 적도록 하겠다.

03. 프로젝트 생성

설명은 한글버전의 Visual Studio 2010 Professional이 기준이다. 새 프로젝트를 생성하기 위해서는 메뉴의 파일(F) – 새로 만들기(N) – 프로젝트(P)를 선택한다.

csharp_login_sample_01

위의 스크린샷과 같은 순서로 Windows Form 응용 프로그램을 선택한 뒤에 프로젝트 이름에는 LoginSample이라고 입력하고 확인버튼을 클릭하면 프로젝트 생성은 완료된다.

04. 메인폼 파일명변경

개인적인 집착인지는 모르겠지만…  샘플을 작업하던, 메인소스를 작업하던 파일명, 네임스페이스, 컨트롤 네이밍은 기존에 세워둔 규칙에 따라서 철저하게 작업하는 편이다. 예전에는 샘플인 경우에는 막 작업했는데 어느 순간부터 습관화를 위해서 해오던 것이 이제는 진짜 습관이 되어버린 것 같다.

csharp_login_sample_02

우측 솔루션 탐색기에서 From1.cs를 선택하고, MainForm.cs로 변경한다. 변경 후에 Enter를 입력하면 아래와 같은 메시지 박스가 나온다.

csharp_login_sample_03

파일이름이 변경되었기 때문에 그와 관련된 내용도 같이 수정할꺼냐는 질의이다. 를 선택하여 파일명 변경작업을 마무리한다.

05. 네임스페이스 변경

이제는 이 샘플프로젝트의 네임스페이스를 변경하려면, 화면 중앙의 폼을 선택한 뒤에 F7을 눌러서 소스편집창으로 이동한다. 소스 상에서 아래의 빨간부분을 참고하여 네임스페이스를 변경한다.

// MainForm.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace <span style="color: #ff0000;"><strong>SIMPLISM.Samples.</strong></span>LoginSample
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }
    }
}

기본적으로 생성된 코드는 한 단계(즉, 프로젝트명)의 네임스페이스를 가지고 있는데, 나의 경우에는 내가 미리 세워둔 규칙에 따라서 프로젝트별로 네임스페이스를 부여해주는 편이다. 지금 수정한 파일은 MainForm.cs 파일이고 정상적으로 프로젝트 전체의 네임스페이스를 변경하려면 MainForm.Designer.cs 파일과 프로젝트의 시작점(Entry Point)인 Program.cs파일을 수정해야한다.

// MainForm.Designer.cs
namespace <span style="color: #ff0000;"><strong>SIMPLISM.Samples.</strong></span>LoginSample
{
    partial class MainForm
    {
        ///
        /// 필수 디자이너 변수입니다.
        ///
        private System.ComponentModel.IContainer components = null;

        // ... 이하 생략 ...

위는 MainForm.Designer.cs 파일을 수정한 것이고.. 이제 마지막으로 전체 프로젝트의 시작점인 Program.cs파일을 아래와 같이 수정하면 된다.

// Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace <span style="color: #ff0000;"><strong>SIMPLISM.Samples.</strong></span>LoginSample
{
    static class Program
    {
        ///

        /// 해당 응용 프로그램의 주 진입점입니다.
        ///
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
    }
}

위와 같이 3개의 파일의 소스에서 네임스페이스를 수정한 뒤에 F6을 눌러서 솔루션을 빌드해서 오류가 발생하지 않으면 정상적으로 수정한 것이다.

06. 로그인 폼 생성

이제 메인폼(MainForm)은 나중에 추가로 작업하고, 메인폼이 뜨기 전에 로그인정보를 입력할 수 있는 로그인용 폼을 만들도록 하겠다.

csharp_login_sample_04

솔루션 탐색기에서 LoginSample 프로젝트를 마우스 오른쪽으로 클릭하면 나타나는 컨텍스트 메뉴(Context Menu)에서 추가 – 새 항목을 선택한다.

csharp_login_sample_05

메뉴를 클릭하면 나오는 화면에서 Windows Form을 선택하고, 이름 LoginForm.cs을 입력하고 추가버튼을 클릭하여 로그인폼 생성을 완료합니다. 위에서 했던 것과 마찬가지로 LoginForm.cs, LoginForm.Designer.cs파일의 네임스페이스를 변경합니다.

07. 로그인 폼 컨트롤배치

아래의 화면을 참고하여 컨트롤들을 화면에 배치한다.

csharp_login_sample_06

각 컨트롤들의 각 속성 값은 아래와 같다.(수정한 것만 기입하면…)

  • Label
    • lblTitle
      • Font : 굴림, 20.25pt, style=Bold, Underline
      • Location : 12, 9
      • Size : 260, 30
      • Text : Login Sample
      • Text Align : MiddleCenter
    • lblID
      • AutoSize : False
      • Font : 굴림, 12pt
      • Location : 14, 46
      • Size : 90, 20
      • Text : ID :
      • TextAlign : MiddleRight
    • lblPassword
      • AutoSize : False
      • Font : 굴림, 12pt
      • Location : 14, 70
      • Size : 90, 20
      • Text : Password :
      • TextAlign : MiddleRight
  • TextBox
    • txbID
      • Location : 110, 46
      • Size : 162, 21
    • txbPassword
      • Location : 110, 69
      • Size : 162, 21
  • Button
    • btnOK
      • Location : 197, 96
      • Size : 75, 23
      • Text : OK
    • btnCancel
      • Location : 17, 96
      • Size : 75, 23
      • Text : Cancel

사실 위와 똑같이 맞출 필요는 없다. 그냥 대충 위치나 컨트롤 이름만 맞으면 문제없다.

 08. 로그인 폼 속성적용

이제는 로그인폼(LoginForm.cs)의 속성을 적용할 차례이다. 아래의 내용을 참고하여 속성을 적용한다.

  • (Name) : frmLogin
  • AcceptButton : btnOK
    • 폼에서 <Enter>키를 누를 때 클릭이벤트를 호출할 버튼
  • CancelButton : btnCancel
    • 폼에서 Cancel 버튼의 역할을 할 버튼
    • 추후에 로그인 폼이 반환할 DialogResult 값 중 DialogResult.Cancel을 반환한다.
  • FormBorderSytle : FixedDialog
    • 그냥 내가 원하는 모양을 위해서…
  • MaximizeBox : False
    • 최대화 버튼 없애기
  • MinimizeBox : False
    • 최소화 버튼 없애기
  • ShowIcon : False
    • 로그인 역할하는 폼에서는 필요없을 것 같아서…
  • ShowInTaskbar : False
    • 로그인 폼은 작업표시줄에 표시하지 않도록 설정
  • StartPosition : CenterScreen
    • 폼 실행 시 주 모니터의 가장 중앙에 폼이 위치하도록 설정
  • Text : Login
    • 로그인 폼의 제목표시줄에는 Login으로 출력되도록 설정
  • TopMost : True
    • 다른 윈도우 중 가장 위에 위치하도록 설정

위의 속성 중에서 나머지는 대부분 모양이나 위치 등을 위해서 설정한 것이고, 가장 중요한 속성은 AcceptButton과 CancelButton 속성이다. 사실 각 버튼의 클릭이벤트를 만들어서 처리를 해도되지만 코딩양을 최소한으로 하기 위해서 위의 속성을 적용했다.

09. 로그인 폼 속성(Property) 작성

로그인 폼에서 메인폼으로 ID와 Password를 전달할 프로퍼티를 만든다.  LoginForm.cs 에 아래의 내용을 추가한다.

        public string ID
        {
            get
            {
                return <span style="color: #ff0000;"><strong>this.txbID.Text</strong></span>;
            }
        }

        public string Password
        {
            get
            {
                return <span style="color: #ff0000;"><strong>this.txbPassword.Text</strong></span>;
            }
        }

 

10. OK버튼 클릭이벤트 추가

마지막으로 OK버튼을 클릭했을 때의 이벤트를 추가한다. 아래와 같은 버튼클릭 이벤트 코드를 작성한다.

private void btnOK_Click(object sender, EventArgs e)
{
    try
    {
        // DialogResult에 OK 값을 넣는다.(알아서 Form은 종료된다.)
        this.DialogResult = <span style="color: #ff0000;"><strong>DialogResult.OK;</strong></span>
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "[ERROR]", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    finally
    {
    }
}

위 처럼 로그인 폼 객체(this, 즉 자신 객체)의 DialogResult 속성(Property)에 DialogResult.OK를 넣으면, 로그인 폼을 종료하면서 DialogResult.OK를 반환할 것이다. 위와 같이 작업된 상태라면 로그인 폼을 준비가 완료된 것이다.(나머지 입력여부 체크라던지 다른 추가로직은 필요에 따라서 넣는다.)

11. 메인폼(MainForm) 코드작성

이제는 실제로 대부분의 작업을 하게될(로그인 폼은 로그인 정보를 입력만 할….) 메인폼을 실행할 때 로그인 폼을 먼저 실행시키는 것이다. 메인폼에서 로그인폼 객체를 생성하고 ShowDialog() 메소드를 호출하면 로그인 폼을 실행할 수 있다. 주의할 점은 이 로그인 폼을 실행할 위치를 결정하는 것이다. 메인폼의 생성자 메소드에서 로그인폼에 대한 호출은 가능하다. 그렇지만, 몇 가지 이유로(로그인 정보가 틀렸다던지… Cancel 버튼을 클릭한다던지) 메인폼 자체를 종료해야할 경우에는 ObjectDisposedException이 발생하므로 메인폼의 Load 이벤트나 Activated 이벤트 호출 시에 로그인 폼을 실행하도록 한다. 아래에 살짝 정리해보자면…

  • 생성자 메소드에 넣을 경우
    • 로그인 폼 실행은 문제없음, 특정 조건에서 메인폼 자체를 종료시켜야할 경우 Exception 발생함.(ObjectDisposedException)
  • Load 이벤트에 넣을 경우
    • 대부분의 경우 가장 적절할 것으로 판단됨
    • 메인폼이 화면에 나타나기 전에 로그인 폼이 나타나고, 로그인 폼이 화면에서 사라지면 그 때 메인폼이 실행된다.
    • 특정 조건에서 메인폼 자체를 종료시켜야할 경우 Exception 처리되지 않고 정상적으로 처리됨
  • Activated 이벤트에 넣을 경우
    • 메인폼이 화면에 나타날 경우 로그인폼이 실행된다.
    • 문제는 매번 메인폼이 활성화(Activated)될 때 마다 로그인 폼이 실행될 수 있으니 추가적으로 제어할 로직이 필요함
    • Load 이벤트와의 차이는 로그인 폼을 메인폼이 뜨기 전에 실행할 지, 반대로 메인폼을 띄우고 로그인폼을 띄울지 결정하기 위함이다.
  • 결론
    • 로그인의 성격상 정삭적으로 로그인 처리가 되지 않은 경우에는 메인폼을 실행할 이유가 없으므로 대부분의 상황에서는 Load 이벤트에 넣는 것이 가장 적절할 것으로 판단된다.

메인폼에서 정상적으로 로그인 폼에서 입력한 정보를 가져오는지 확인하기 위해서 아래의 내용을 Load 이벤트에 넣어준다.

login = new frmLogin();
DialogResult dlgResult = login.ShowDialog();
if (dlgResult == DialogResult.OK)
{
    MessageBox.Show("ID = " + <strong><span style="color: #ff0000;">login.ID</span></strong> + "rn" + "PW = " + <span style="color: #ff0000;"><strong>login.Password</strong></span>);
}
else
{
    this.Close(); // 생성자에서 이 메소드를 호출하면 Exception 발생
}

 12. 마치면서…

사실 별 내용은 없는데… 막상 설명하려고하니 분량이 너무 많아진다. 설명할 능력이 없어서 그런 것일 수도 있고… 위에서 핵심적인 내용은 아래 내용 뿐이다.

  1. 로그인폼에 속성(Property)를 만드는 것(또는 getter 메소드를 만드는 것)
    • 로그인폼에서 입력한 정보를 메인폼에서 가져오기 위해서…
  2. 메인폼의 Load 이벤트에서 로그인폼을 실행하고, 입력한 값을 가져와서 로그인 작업을 하는 것

단지 위의 2가지 핵심내용만 설명하면 됬는데… 다 쓰고나니 너무 장황하게 써버렸다…ㅡㅡ;; 그리고 위의 설명으로 부족한 내용은 아래 첨부한 소스코드를 참고하시길 바란다.

    • 그렇군요..ㅎㅎ;; 왜 program.cs에서 처리한다는 생각은 못 했을까요…ㅡ.ㅡ;;

    • 포스트 하단에 샘플프로젝트 첨부해놓은 것을 다시 다운받아서 확인해보니 메인폼에서 Load이벤트 시에 로그인용 폼부터 띄우도록 작성되어있습니다. 그리고 로그인 폼이 DialogResult.OK를 반환하면 메시지박스로 로그인폼의 Property인 ID와 Password를 읽어와서 메시지박스로 출력하도록 작성되어있습니다. 메인에 전달하지 못한다는게 무슨 의미인지요…ㅎ 정확하게 표현하자면 제가 작성한 코드는 메인이 로그인폼의 값을 읽어오도록 만들어져있습니다. 윗 댓글 남겨주신 분 말씀처럼 메인폼의 Load이벤트에서 처리하지 않고, 프로젝트 Entry함수인 Main()에서 처리하는 방법도 있습니다.

  • ^^

    소스코드가 다운이 안돼요.