본문 바로가기
인공지능/데이터분석

DB를 이용해 단어장 만들기

by hyunji00pj 2024. 10. 29.

지금 까지 배운 파이썬 문법과 MySQL 쿼리문 등을 이용해 DataBase를 활용한 단어장을 만들어 보려고 한다

이번에 단어장을 만들때엔 객체지향적으로 코드를 작성하기 위해서 class를 많이 활용하고

데이터의 무결성을 위해서 객체의 데이터를 안전하게 캡슐화 하기위해 getter와 setter 메서드를 사용할 것이다.

 

객체지향 프로그래밍

객체지향 프로그래밍(Object-Oriented Programming, OOP)은 소프트웨어를 보다 유연하고, 유지보수가 용이하며, 확장성이 높은 방식으로 설계할 수 있도록 돕는 프로그래밍 패러다임이다.

 

객체지향적인 코드를 짜기 위해서 필요한 몇가지 원칙이 있다.

1. 클래스와 객체 이해

  • 클래스(Class): 객체를 생성하기 위한 템플릿 클래스는 객체의 기본 형태와 기능을 정의함
  • 객체(Object): 클래스에 기반하여 생성된 인스턴스이다. 각 객체는 클래스로부터 상속받은 속성(데이터)과 메서드(함수)를 가지고 있다.

2. 캡슐화(Encapsulation)

  • 데이터(속성)와 데이터를 조작하는 메서드를 하나의 클래스 단위로 묶는 것
  • 클래스 내부의 데이터는 외부에서 직접 접근할 수 없도록 숨기고(Private 또는 Protected 접근 제한자 사용), 필요한 경우에만 메서드를 통해 접근할 수 있도록 함
  • 이를 통해 객체의 상세 구현을 숨기고, 객체 간의 결합도를 낮추며, 인터페이스만을 통한 상호 작용을 유도

3. 상속(Inheritance)

  • 한 클래스가 다른 클래스의 속성과 메서드를 상속 받을 수 있음
  • 코드 재사용성을 높이고, 유지보수를 간소화하는 데 유용함
  • 상속 계층을 통해 공통의 로직을 상위 클래스에 두고, 각 하위 클래스에서는 특화된 기능만을 추가 또는 변경

4. 다형성(Polymorphism)

  • 같은 이름의 메서드가 다른 동작을 하는 것을 의미
  • 메서드 오버로딩(같은 이름의 메서드를 매개변수에 따라 다르게 동작하도록 함)과 메서드 오버라이딩(상속 받은 메서드의 기능을 하위 클래스에서 변경함)을 통해 구현
  • 인터페이스와 추상 클래스를 사용하여 다양한 객체들이 같은 인터페이스를 공유하면서도 각기 다른 행동을 할 수 있도록 함

5. 추상화(Abstraction)

  • 복잡한 내부 구현 세부사항을 숨기고, 필요한 부분만을 간추려 표현하는 것
  • 추상 클래스나 인터페이스를 사용하여 공통적인 특징을 모델링하고, 구체적인 구현은 하위 클래스에서 수행

6. 디자인 패턴(Design Patterns)

  • 반복적으로 나타나는 문제들을 해결하기 위한 잘 정의된 솔루션
  • 디자인 패턴을 학습하고 적절한 상황에서 적용함으로써, 보다 견고하고 유지보수가 용이한 코드를 작성할 수 있음

7. SOLID 원칙

  • 객체지향 설계의 다섯 가지 기본 원칙으로, 좋은 소프트웨어 아키텍처를 설계하는 데 도움을 줌
    • Single Responsibility Principle: 한 클래스는 하나의 책임만 가져야 합니다.
    • Open/Closed Principle: 클래스는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 합니다.
    • Liskov Substitution Principle: 하위 타입은 항상 그 상위 타입으로 대체될 수 있어야 합니다.
    • Interface Segregation Principle: 사용하지 않는 인터페이스는 클라이언트에 강제되어서는 안 됩니다.
    • Dependency Inversion Principle: 의존성은 구체적이 아닌 추상화에 맞춰야 합니다.

객체지향적으로 코드를 작성하는 것은 단순히 기술적인 스킬을 넘어선 설계 철학의 이해를 필요로 한다. 이 원칙들을 지키며 코드를 작성할 때, 프로그램은 확장성 있고 유지보수가 용이하며, 오류가 적은 형태로 발전할 수 있다

 

위 객체 지향 프로그래밍에서 설명한 캡슐화(Encapsulation)를 위해서 아래 코드에서는 getter와 setter메서드를 사용해 속성에 대한 접근과 변경을 보다 안전하고 효율적으로 관리 할 수 있게 할것이다.

Getter (접근자)

  • Getter는 클래스의 특정 속성 값을 검색하거나 반환하는 메소드
  • 이 메소드는 주로 속성값을 읽을 때 사용되며, 외부에서 직접적으로 속성에 접근하는 것을 방지
  • Python에서는 @property 데코레이터를 사용하여 getter를 정의할 수 있음

Setter (설정자)

  • Setter는 클래스의 특정 속성에 값을 설정하거나 변경하는 메소드
  • 이 메소드는 속성값을 변경할 때 사용되며, 값의 유효성 검사나 추가적인 처리가 필요한 경우에 유용함
  • Python에서는 setter를 정의할 때 @property_name.setter 데코레이터를 사용함

 

위 내용을 기반으로 객체지향적으로 단어장을 만들어 보겠다

 

 

1. 필요한 모듈 import

먼저 필요한 모듈을 import 해준다

import MySQLdb

2. class Words

class Words에서는 영어단어와 그에 해당하는 한국어 뜻, 단어의 난이도를 속성으로 가지고 있다

class Words:
    def __init__(self,eng,kor,lev=1):  #Words 클래스 생성자 객체가 생성될때 호출되어 객체의 초기 상태를 설정
    #self : 객체 자신을 가리키는 참조    
        self.eng = eng
        self.kor = kor
        self.lev = lev # 기본값 = 1로 설정되어있음
        
    # 각각 eng와 kor, lev는 getter 메서드로 ex)self.eng 속성 값을 반환하며,
    # setter 메서드로 각 객체를 설정해 ex)self.eng속성을 새로운 eng값으로 업데이트함
    def setEng(self,eng): 
        self.eng = eng
    def getEng(self):
        return self.eng
        
    def setKor(self,kor):
        self.kor = kor
    def getKor(self):
        return self.kor
        
    def setLev(self,lev):
        self.lev = lev
    def getLev(self):
        return self.lev

3. class WordsDao

단어장의 각각의 기능들을 정의해둔 클래스입니다.

 

class WordsDao:
    def __init__(self): #WordsDao 생성자이다
        self.db = None # db연결을 위한 변수를 초기화 해준다
        
    def connect(self): #db에 연결해주는 함수
        self.db = MySQLdb.connect('localhost','root','1234','kdt')
        #MySQL DataBase kdt에 localhost인 비밀번호 1234 인 root계정으로 연결
        
    def disconnect(self):#데이터베이스 연결 헤제 함수
        self.db.close() #연결된 db 를 닫음
        
    def insert(self,word):#단어를 삽입하는 함수
        self.connect() #db에 연결
        cur = self.db.cursor() # 커서 객체 생성
        sql = 'insert into voca (eng,kor,lev) values (%s,%s,%s)'
        # sql쿼리문, voca 테이블에 eng,kor,lev값을 삽입
        data = (word.getEng(),word.getKor(),word.getLev())
        # word 객체에서 영어단어,뜻,레벨 정보를 가져와 data에 할당함
        cur.execute(sql,data)#쿼리 실행
        self.db.commit()#변경사항 DB에 커밋
        cur.close()#커서 닫기
        self.disconnect()#DB 연결 해제
        
    def selectAll(self):#DB에 저장된 모든 단어 정보를 조회하는 함수
        self.connect()
        cur = self.db.cursor(MySQLdb.cursors.DictCursor)
        sql = 'select eng,kor,lev from voca order by eng asc'
        #voca테이블에서 eng필드(영어단어) 알파벳 순으로 정렬하여 조회
        cur.execute(sql)#쿼리 실행
        row = cur.fetchall()# 모든 결과 가져오기
        cur.close()
        self.disconnect()
        return row #위 row객체에 저장된 조회 결과 반환
        
    def search(self,eng):# 영단어 검색해 해당 영단어 정보 조회하는 함수
        self.connect()
        cur = self.db.cursor(MySQLdb.cursors.DictCursor)
        sql = "select eng,kor,lev from voca where eng like concat('%%',%s,'%%')"
        #eng값을 받아서 SQL문을 통해 해당 영단어를  select
        data = (eng,)
        cur.execute(sql,data)
        row = cur.fetchall()#해당 영단어 반환
        cur.close()
        self.disconnect()
        return row
    def update(self,word):#영단어 정보 수정하기 기능
        self.connect()
        cur = self.db.cursor()
        sql = 'update voca set kor= %s, lev = %s where eng = %s'
        data = (word.getKor(),word.getLev(),word.getEng())
        #word에 캡슐화된 값들 불러와서 SQL문을 통해서 수정
        result = cur.execute(sql,data)
        self.db.commit()
        if result > 0:
            print('수정되었습니다')
        else:
            print('에러!')
            #if-else문을 통해 만약 영단어가 없다면 '에러!' 출력하도록 설정
        cur.close()
        self.disconnect()
        
    def delete(self,eng):#영단어 삭제하기 기능
        self.connect()
        cur = self.db.cursor()
        sql = 'delete from voca where eng=%s'
        data = (eng,)
        #삭제할 영단어 값을 eng로 받아서 SQL문으로 삭제
        result = cur.execute(sql,data)
        self.db.commit()
        if result > 0:
            print('삭제되었습니다')
        else:
            print('단어가 존재하지 않습니다')
            if-else문을 통해서 삭제할 영단어가 없다면 '단어가 존재하지 않습니다' 출력
        cur.close()
        self.disconnect()

인스턴스(instance)는 객체 지향 프로그래밍에서 특정 클래스의 구체적인 실체를 의미. 클래스는 객체의 설계도와 같은 역할을 하며, 이 설계도를 바탕으로 생성된 각각의 객체를 인스턴스라고 함. 클래스는 객체의 속성과 행위를 정의하지만, 실제로 메모리에 할당되어 동작하는 것은 인스턴스이다.

class WordsService:
    def __init__(self):
        self.dao = WordsDao()
        # WordsDao 클래스의 인스턴스를 생성하여 self.dao에 할당
#WordsDao 클래스의 인스턴스를 멤버 변수로 포함해서
#WordsDao의 메서드를 직접 호출하여 데이터베이스 관련 작업을 수행

    def insertWord(self):
        eng = input('단어를 입력하세요')
        kor = input('뜻을 입력하세요')
        lev = input('레벨을 입력하세요')
        word = Words(eng,kor,lev) # 입력받은 정보로 Words 클래스의 인스턴스를 생성
        self.dao.insert(word)# 생성된 Words 인스턴스를 dao의 insert 메서드를 사용하여 데이터베이스에 저장

    def printAll(self):
        datas = self.dao.selectAll()
        for data in datas:
            print(f"{data['eng']},뜻:{data['kor']},레벨{data['lev']}")
            # 검색된 단어 정보를 출력 각 단어의 영어, 한국어 뜻, 레벨을 포맷에 맞게 출력
    def searchWord(self):
        eng = input('검색할 단어를 입력하세요')
        datas = self.dao.search(eng)
        if datas:
            for data in datas:
                print(f"{data['eng']},뜻:{data['kor']},레벨{data['lev']}")
                # 입력받은 단어를 포함하는 모든 정보를 데이터베이스에서 검색
        else:
            print('찾는 단어가 없습니다')
             # 검색된 결과가 없으면 메시지 출력
    def editWord(self):
        eng = input('수정할 단어를 입력하세요')
        word = self.dao.search(eng)
        # 입력받은 단어에 해당하는 정보를 데이터베이스에서 검색
        if not word:
            print('수정할 단어가 없습니다')
            # 검색된 단어가 없으면 메시지를 출력
        else:
            kor = input('새로운 뜻을 입력하세요')
            lev = input('새로운 레벨을 입력하세요')
            word = Words(eng,kor,lev)
            # 새로운 eng,kor,lev로 Words 인스턴스를 생성
            self.dao.update(word)
            # 생성된 인스턴스로 데이터베이스 정보를 업데이트
            
    def delWord(self):
        eng = input(' 삭제할 단어를 입력하세요')
        word = self.dao.search(eng)
        if not word:
            print('삭제할 단어가 없습니다')
        else:
            self.dao.delete(eng)
            # 검색된 단어를 데이터베이스에서 삭제

class Menu 는 사용자 인터페이스를 담당하며, 사용자가 선택한 메뉴에 따라 서비스 로직을 호출함

class Menu:
    def __init__(self):
        self.service = WordsService()
         # WordsService 클래스의 인스턴스를 생성하고, self.service에 할당
    def run(self):
        while True:
         # 무한 루프를 사용하여 사용자가 '종료하기'를 선택할 때까지 프로그램이 계속 실행되도록 함
            try:
                menu = int(input('1.등록하기 2.출력하기 3.검색하기 4. 수정하기 5. 삭제하기 6.종료하기'))
                if menu == 1:
                    self.service.insertWord()
                elif menu == 2:
                    self.service.printAll()
                elif menu == 3:
                    self.service.searchWord()
                elif menu == 4:
                    self.service.editWord()
                elif menu == 5:
                    self.service.delWord()
                elif menu == 6:
                    print('프로그램을 종료합니다')
                    break # while 루프를 탈출하여 프로그램을 종료
            except Exception as e:
                print(e)  # 예외가 발생했을 경우 예외 정보를 출력
                print('다시 입력하세요')# 사용자에게 입력 오류를 알리고 다시 입력을 요청

사용자가 메뉴를 선택할 때마다 적절한 WordsService 메서드를 호출하여 단어의 등록, 조회, 검색, 수정, 삭제와 같은 기능을 수행하도록 함

#실행문
start = Menu()
start.run()