파이썬/파이썬(python) 중급

[30-6 파이썬] 패스워드 매니저 만들기(json, 예외처리, 기능추가, if~else와 비교)

Olivia-BlackCherry 2022. 9. 20. 07:36

패스워드 매니저 만들기를 업그레이드한다.

- 데이터 저장하는 방식을 json으로 바꾸기
- 예상되는 오류에 대한 예외처리
- 사용자에게 편리하도록 기능(search)을 추가
- if~else와 예외처리를 비교

https://olivia-blackcherry.tistory.com/183

 

[29-2 파이썬] tkinter 패스워드 매니저 만들기

tkinter를 이용해서 웹사이트, 아이디, 비밀번호를 저장하여 필요할 때마다 확인할 수 있는 나만의 패스워드 매니저를 만들어보자. 1. tkinter기본 window만들기 mainloop() : 화면 꺼지지 않고, 계속 작동

olivia-blackcherry.tistory.com

 

1. 데이터 저장 방식 json으로 변경하기

save_data() 함수 부분만 수정되었다.

from tkinter import *
from tkinter import messagebox
import random
import pyperclip
import json

print('#'.join(['a','b','c']))
# ---------------------------- PASSWORD GENERATOR ------------------------------- #
def safe_password():
    letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
    numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    symbols = ['!', '#', '$', '%', '&', '(', ')', '*', '+']

    nr_letters = random.randint(8, 10)
    nr_symbols = random.randint(2, 4)
    nr_numbers = random.randint(2, 4)

    letters_list = [random.choice(letters) for char in range(nr_letters)]
    symbols_list = [random.choice(symbols) for char in range(nr_symbols)]
    numbers_list = [random.choice(numbers) for char in range(nr_numbers)]

    password_list= letters_list + symbols_list + numbers_list
    random.shuffle(password_list)

    password = ''.join(password_list)

    password_input.insert(0,password)

    pyperclip.copy(password)
# ---------------------------- SAVE PASSWORD ------------------------------- #
def save_data():
    save_website = website_input.get()
    save_email= email_username_input.get()
    save_password = password_input.get()

    save_all = f"{save_website}, {save_email}, {save_password}"
    new_data = {
        save_website:{
            "email": save_email,
            "password": save_password
        }
    }

    if len(save_website)<1 or len(save_email)<1 or len(save_password)<1:
        messagebox.showerror(title="에러", message="입력한 값이 너무 짧습니다")

    else:
        is_ok = messagebox.askokcancel(title= save_website, message=f"이대로 저장해도 될까요?\n{save_all}")
        if is_ok:
            with open(file="data.json") as f:
                new_json_data = json.load(f)
                new_json_data.update(new_data)

            with open(file = "data.json", mode="w") as f:
                json.dump(new_json_data, f, indent=4)

            website_input.delete(0, END)
            email_username_input.delete(0, END)
            password_input.delete(0, END)


# ---------------------------- UI SETUP ------------------------------- #
window = Tk()
window.config(padx=50, pady=50, bg="white")
window.title("my password manager")


canvas = Canvas(width=200, height=200, bg= "white", highlightthickness=0)
picture = PhotoImage(file = "paswd.png")
picture = picture.subsample(2,2)
canvas.create_image(100, 90, image=picture)
canvas.grid(row=0, column=1)

#Label
website = Label(text="website", font=("Courier", 10, "bold"), bg="white")
website.grid(row= 1, column=0)
email_username = Label(text="Email/Username", font=("Courier", 10, "bold"), bg="white")
email_username.grid(row= 2, column=0)
password = Label(text="password", font=("Courier", 10, "bold"), bg="white")
password.grid(row= 3, column=0)


#Entry
website_input= Entry(width=40)
website_input.grid(row=1, column=1, columnspan=2)
website_input.focus()

email_username_input= Entry(width=40)
email_username_input.grid(row=2, column=1, columnspan=2)
email_username_input.insert(0, "olivia@coding.co.kr")

password_input=Entry(width=24)
password_input.grid(row=3, column=1)


#Button
generate_password = Button(text="Generate Password", width=15, command=safe_password)
generate_password.grid(row=3, column=2)
add = Button(text="Add", width=40, command=save_data)
add.grid(row=4, column=1, columnspan=2)

window.mainloop()

 

 

2. save_data() 함수 예외처리하기

FileNotFound Error에 대한 예외처리를 한다.

from tkinter import *
from tkinter import messagebox
import random
import pyperclip
import json

print('#'.join(['a','b','c']))
# ---------------------------- PASSWORD GENERATOR ------------------------------- #
def safe_password():
    letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
    numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    symbols = ['!', '#', '$', '%', '&', '(', ')', '*', '+']

    nr_letters = random.randint(8, 10)
    nr_symbols = random.randint(2, 4)
    nr_numbers = random.randint(2, 4)

    letters_list = [random.choice(letters) for char in range(nr_letters)]
    symbols_list = [random.choice(symbols) for char in range(nr_symbols)]
    numbers_list = [random.choice(numbers) for char in range(nr_numbers)]

    password_list= letters_list + symbols_list + numbers_list
    random.shuffle(password_list)

    password = ''.join(password_list)

    password_input.insert(0,password)

    pyperclip.copy(password)
# ---------------------------- SAVE PASSWORD ------------------------------- #
def save_data():
    save_website = website_input.get()
    save_email= email_username_input.get()
    save_password = password_input.get()

    save_all = f"{save_website}, {save_email}, {save_password}"
    new_data = {
        save_website:{
            "email": save_email,
            "password": save_password
        }
    }

    if len(save_website)<1 or len(save_email)<1 or len(save_password)<1:
        messagebox.showerror(title="에러", message="입력한 값이 너무 짧습니다")

    else:
        is_ok = messagebox.askokcancel(title= save_website, message=f"이대로 저장해도 될까요?\n{save_all}")
        if is_ok:
            try:
                with open(file="data.json") as f:
                    new_json_data = json.load(f)

            except FileNotFoundError:
                with open(file="data.json", mode="w") as f:
                    json.dump(new_data, f, indent=4)

            else:
                new_json_data.update(new_data)
                with open(file = "data.json", mode="w") as f:
                    json.dump(new_json_data, f, indent=4)

            finally:
                website_input.delete(0, END)
                email_username_input.delete(0, END)
                password_input.delete(0, END)


# ---------------------------- UI SETUP ------------------------------- #
window = Tk()
window.config(padx=50, pady=50, bg="white")
window.title("my password manager")


canvas = Canvas(width=200, height=200, bg= "white", highlightthickness=0)
picture = PhotoImage(file = "paswd.png")
picture = picture.subsample(2,2)
canvas.create_image(100, 90, image=picture)
canvas.grid(row=0, column=1)

#Label
website = Label(text="website", font=("Courier", 10, "bold"), bg="white")
website.grid(row= 1, column=0)
email_username = Label(text="Email/Username", font=("Courier", 10, "bold"), bg="white")
email_username.grid(row= 2, column=0)
password = Label(text="password", font=("Courier", 10, "bold"), bg="white")
password.grid(row= 3, column=0)


#Entry
website_input= Entry(width=40)
website_input.grid(row=1, column=1, columnspan=2)
website_input.focus()

email_username_input= Entry(width=40)
email_username_input.grid(row=2, column=1, columnspan=2)
email_username_input.insert(0, "olivia@coding.co.kr")

password_input=Entry(width=24)
password_input.grid(row=3, column=1)


#Button
generate_password = Button(text="Generate Password", width=15, command=safe_password)
generate_password.grid(row=3, column=2)
add = Button(text="Add", width=40, command=save_data)
add.grid(row=4, column=1, columnspan=2)

window.mainloop()

 

 

3. 데이터 파일에서 저장 정보 찾는 버튼 만들기

website를 입력하고 Search 버튼을 누르면 해당 id, password가 나오도록 만든다.

def info_search():
    save_website = website_input.get()
    with open(file="data.json") as f:
        for_search_data = json.load(f)
        search_email = for_search_data[save_website]["email"]
        search_password = for_search_data[save_website]["password"]
        messagebox.showinfo(title="your email and password",
                            message=f"email: {search_email}, password: {search_password}")
search = Button(text="Search", width=15, command=info_search)
search.grid(row=1, column=2)

 

 

4. info_search()함수 예외처리

FileNotFoundError와 KeyError 

def info_search():
    save_website = website_input.get()
    try:
        with open(file ="data3.json") as f:
            for_search_data = json.load(f)
    except FileNotFoundError:
        messagebox.showinfo(title="경고", message="저장된 정보가 없습니다. 새로운 정보를 저장해주세요")
    else:
        try:
            search_email = for_search_data[save_website]["email"]
            search_password = for_search_data[save_website]["password"]
        except KeyError:
            print("키에러")
            messagebox.showinfo(title="Warning", message="No infomation")
        else:
            print("성공")
            messagebox.showinfo(title="your email and password", message=f"email: {search_email}, password: {search_password}")

 

 

5. 예외처리 대신 if~else 구문 쓰기

사실 try~except~else 구문은 

if~ else와 그 원리가 같다. 

두 상황을 좀더 비교 설명해보면 

 

예외 exception적인 상황은

매번 일어나는 것이 아니라 꽤 드물게 일어나는 것이라고 본다. 

 

반면 if~else는 생활 속에서 아주 빈번하게 일어나는 경우의 수라고 볼 수 있다. 

 

따라서 

if~else로 처리할 수 있다면, 가급적 if~else로 쓰는 것이 좋고,

예외처리 밖에 사용할 수 없는 경우, 

즉 다른 쉬운 대안이 없을 때만 try~except~else~finally 구문을 쓰면 좋다.

def info_search():
    save_website = website_input.get()
    try:
        with open(file ="data.json") as f:
            for_search_data = json.load(f)
    except FileNotFoundError:
        messagebox.showinfo(title="경고", message="저장된 정보가 없습니다. 새로운 정보를 저장해주세요")
    else:
        if save_website in for_search_data:
            search_email = for_search_data[save_website]["email"]
            search_password = for_search_data[save_website]["password"]
            messagebox.showinfo(title="your email and password",
                                message=f"email: {search_email}, password: {search_password}")
        else:
            messagebox.showinfo(title="Warning", message="No infomation")