C 프로그래밍

C언어 프로젝트 23.12.03

Nohsw 2023. 12. 3. 10:21
#include <stdio.h>
#include <conio.h> // getch() 함수를 사용하기 위한 헤더 파일
#include <string.h> //문자열 비교함수인 "strcmp"를 사용하기 위해 string.h 라이브러리를 사용.
//#include <bcrypt.h>

// 무한 루프 종료 조건을 위한 상수
#define LOGOUT_OPTION -1

enum loginProgram {
    userCreate = 1, // 회원가입
    userLogin = 2,  // 로그인
};

struct userInfo { // struct를 이용하여 로그인 정보 저장
    char userId[100];
    char userPassword[100];
    char userCheckPassword[100];
};

struct userInfoCompare { // struct를 이용하여 로그인 시 아이디, 비밀번호 비교
    char userId[100];
    char userPassword[100];
};

//// 비밀번호를 bcrypt 해시화하는 함수
//void hashPassword(const char* password, char* hashedPassword) {
//    if (bcrypt_hashpw(password, "$2a$12$SOME_FIXED_SALT$", hashedPassword) != 0) {
//        // 해시화 실패 처리
//        fprintf(stderr, "비밀번호 해시화에 실패했습니다.\n");
//    }
//}

void clearInputBuffer() { //사용자가 문자열 입력 시 무한루프 방지
    int c;
    while ((c = getchar()) != '\n' && c != EOF); //입력 버퍼를 비워주는 역할
}

void displayMainMenu() {
    printf("\n\n");
    printf("메뉴를 선택하세요.\n");
    printf("종료하시려면 '%d'을 눌러주십시오.\n", LOGOUT_OPTION);
    printf("[1. 메뉴1]\n[2. 메뉴2]\n");
    printf("입력: ");
}

void getPassword(char* password) { //비밀번호 비교를 위한 함수
    char ch;
    int i = 0; //사용자가 입력한 문자열 갯수

    while (1) {
        ch = _getch();

        if (ch == 13) { // Enter 키를 누르면 종료
            break;
        }
        
        else if (ch == 8) {
            if (i > 0) {
                printf("\b \b"); // 백스페이스 키 입력 시, 화면에서 지움
                i--;
            }
        }
        
        else {
            password[i++] = ch; // 비밀번호 배열에 문자 추가
            printf("*"); // 별표 출력
        }
    }

    password[i] = '\0'; // 문자열 종료를 위한 널 문자 추가
    printf("\n");
}

int main() {
    enum loginProgram login_Type; // 사용자의 선택으로 로그인 프로그램 실행.
    struct userInfo userCreateInfo;
    struct userInfoCompare userLoginInfo;

    do {
        printf("로그인 프로그램\nBy.Nohsw");
        printf("\n\n");
        printf("[1. 회원가입]\n[2. 로그인]\n");
        printf("입력: ");
        scanf_s("%d", &login_Type); // 사용자의 선택 값으로 switch 실행.
        printf("\n\n");

        switch (login_Type) { //가독성 향상을 위해 enum 사용하였고, switch case를 작성.
        case userCreate: //회원가입
            printf("회원가입 화면입니다.\n");

            printf("회원가입 할 ID를 입력해주세요.\n");
            printf("입력: ");
            scanf_s("%99s", userCreateInfo.userId, sizeof(userCreateInfo.userId));
            //%99s를 사용한 이유: 버퍼 오버플로우를 방지하기 위해 입력받을 문자열의 크기를 명시를 해야한다.
            // 버퍼 오버플로우: 버퍼 오버플로우는 배열의 크기를 초과하여 데이터를 저장하려고 할 때 발생하는 문제입니다.
            // 이러한 문제를 방지하기 위해서는 배열의 크기를 확인하고, 데이터를 저장하기 전에 배열의 크기를 초과하지 않도록 조치해야 합니다.

            //scanf_s 사용을 위해서는 크기 제한을 둔 %s를 사용하는게 좋다. (메모리 누수를 포함한 보안 문제를 방지하기 위함)
            //위에 userId[100]으로 작성하였기에 null값인 \0을 제외한 %99s를 사용.

            printf("비밀번호를 입력해주세요.\n");
            printf("입력: ");
            getPassword(userCreateInfo.userPassword);

            // 비밀번호를 bcrypt 해시화하여 저장
            //hashPassword(userCreateInfo.userPassword, userCreateInfo.userPassword);

            printf("비밀번호를 한번 더 입력해주세요.\n");
            printf("입력: ");
            getPassword(userCreateInfo.userCheckPassword); //함수를 이용하여 1차와 2차 비밀번호 비교
            //hashPassword(userCreateInfo.userCheckPassword, userCreateInfo.userCheckPassword);

            //비밀번호 비교 후 맞으면 회원가입 성공, 틀리면 회원가입 실패 출력.
            if (strcmp(userCreateInfo.userPassword, userCreateInfo.userCheckPassword) == 0) {
                printf("회원가입 성공.\n");
                printf("사용자의 아이디는 %s입니다.\n", userCreateInfo.userId);
            }
            else {
                printf("회원가입 실패.\n");
                printf("사유: 입력한 비밀번호가 다릅니다.\n");
            }

            printf("\n\n");
            printf("ID: %s PS: %s", userCreateInfo.userId, userCreateInfo.userPassword);
            break;

        case userLogin: //로그인
            printf("아이디 입력: ");
            scanf_s("%99s", userLoginInfo.userId, sizeof(userLoginInfo.userId));
            printf("비밀번호 입력: ");
            getPassword(userLoginInfo.userPassword);

            if (strcmp(userLoginInfo.userId, userCreateInfo.userId) == 0 &&
                strcmp(userLoginInfo.userPassword, userCreateInfo.userPassword) == 0) {
                printf("로그인 성공\n");

                int inputNum; //로그인 이후 화면에서 사용자가 입력할 값
                int i; //반복문 사용하여 여백 주기 위한 변수
                
                do { //로그인 성공 후 상단에 여백을 주기위한 반복문
                    for (i = 0; i < 21; i++) {
                        printf("\n");
                    }
              
                    printf("%s님 안녕하세요.\n", userCreateInfo.userId);
                    displayMainMenu();
                    scanf_s("%d", &inputNum);

                    switch (inputNum) {
                    case LOGOUT_OPTION:
                        printf("\n\n");
                        printf("로그아웃을 진행합니다.\n\n");
                        break;

                    case 1:
                        printf("메뉴1\n");
                        break;

                    case 2:
                        printf("메뉴2\n");
                        break;

                    default:
                        printf("\n\n");
                        printf("잘못된 값을 입력하였습니다. 다시 입력해주세요.\n");
                        clearInputBuffer(); //문자열 입력 시 무한루프를 방지하는 함수. 24번 참고.
                        break;
                    }
                    
                } while (inputNum != LOGOUT_OPTION);
            }
            else {
                printf("\n\n");
                printf("로그인 실패\n");
                printf("\n\n");
            }
            break;
        }

    } while (1);

    return 0;
}

어제 발생한 버그에 대한 부분은 코드 수정 후 사라진 것 같다.

ClearInputBuffer 함수를 만들어 문자열 입력 시 무한루프에 빠지는 부분을 해결하였고, 해결하는 방법은 입력버퍼를 비워주면 된다고 하여 해결하였다.

또한 초기에 아이디와 비밀번호가 맞지 않았을 때 else문으로 넘어갔던 방법은 기존 코드를 다 지우고 if에서는 로그인 성공, else 로그인 실패로 변경하였다.

비밀번호 암호화는 진행하려했으나 오류가 발생해 일단 주석처리를 진행하였고, 암호화까지 완료하면 혼자 생각했던 작은 프로젝트는 거의 끝나갈 것 같다.

이번 프로젝트를 통해 많은 것을 도전하여 모르는 부분을 많이 배우게 되었고, 앞으로도 작은 프로젝트를 도전하여 생각해본 것들을 꾸준히 만들 예정이다. 이제 암호화에 대해 공부해보고 비밀번호 암호화를 끝으로 다른 프로젝트를 생각해봐야겠다.

 

앞으로 추가해야 할 목록:

1. 비밀번호 암호화.

2. 회원가입 시 사용자 아이디 추가.

(현재는 회원가입 후 다시 회원가입을 하면 기존에 있던 아이디와 비밀번호를 덮어씌우는 문제가 있다. 이러한 문제를 해결하기 위해 덮어씌우는 방법이 아닌 계속 추가되는 방법으로 구현 할 예정)

'C 프로그래밍' 카테고리의 다른 글

C프로그래밍 23.12.04 리눅스 연습  (0) 2023.12.04
C 프로그래밍 우분투 시작  (2) 2023.12.03
C언어 프로젝트 23.12.02  (2) 2023.12.02
C언어 프로젝트 23.12.01 (1)  (5) 2023.12.01
C언어 프로젝트 23.12.01  (0) 2023.12.01