수업 13일차

galoo·2023년 7월 13일
0

HelloVision Dx Data School

목록 보기
13/72
post-thumbnail

✔ 예외 처리

예외

  • 문법적인 오류는 없어서 실행은 되지만 실행도중 발생한 예기치 않은 상황으로 인해 프로그램이 중단되는 현상
  • 프로그램을 잘못 짰거나 / 어떻게 할 수 없는(메모리 부족) 문제

예외처리

  • 예외가 발생했을 때 비정상 종료를 하지 않고 정상적인 수행을 하도록 처리하는 것

기본 형식

try :
	예외가 발생할 가능성이 있는 구문
except : 
	예외가 발생했을 때 수행할 내용
#예외가 어디에서 발생하는지 확인을 먼저 하자.
ar=[10,20,30]
#문자 혹은 0을 넣으면 여러 예외 발생!
try:
    num=int(input("나눌 수를 입력하세요"))
    i=0
    #len(ar)은 변하지 않음(고정), 비효율적임
    #왜냐? 함수를 통해 가져오기 때문이다.
    j=len(ar)
    #이렇게 하면 함수 호출을 한 번만 하기에
    #메모리 낭비가 없이 효율적이다.
    while i<j: 
        print(ar[i]/num)
        i=i+1
except:
    print("예외 발생!")

예외를 종류 별로 처리하기

  • except 다음에 예외 클래스 이름을 기재한 후 처리하는 코드를 작성하면 예외 클래스 이름에 해당하는 예외가 발생했을 때 구문을 수행한다.
  • except는 여러 개 작성이 가능한데 if ~ elif 처럼 동작합니다.
  • 실제로는 하나만 수행되는 것입니다.
#예외가 어디에서 발생하는지 확인을 먼저 하자.
ar=[10,20,30]
#문자 혹은 0을 넣으면 여러 예외 발생!
try:
    num=int(input("나눌 수를 입력하세요"))
    i=0
    #len(ar)은 변하지 않음(고정), 비효율적임
    #왜냐? 함수를 통해 가져오기 때문이다.
    j=len(ar)
    #이렇게 하면 함수 호출을 한 번만 하기에
    #메모리 낭비가 없이 효율적이다.
    while i<j: 
        print(ar[i]/num)
        i=i+1
except ValueError:
    print("잘못된 데이터 입력!")
except ZeroDivisionError:
    print("0으로는 나눌 수 없습니다!")
#예외는 value 에러와 zerodivision 에러가 있는데
#한 곳에서 처리하고 있다.

예외 발생 사실만을 아는 것 뿐만 아니라, 어떤 예외가 발생하였는지 알 수 있다.

예외 내용을 받아서 처리하기

  • except 예외클래스이름 as 변수명 :
    - 위의 경우에는 예외 내용을 변수에 대입하게 된다.
    - 변수를 출력하면 어떤 예외가 발생했는지 알 수 있다.
except ValueError as e:
    print(e)
except ZeroDivisionError as e:
    print(e)

invalid literal for int() with base 10: '1d'
division by zero

예외 클래스 계층


  • 예외처리는 내가 해결할 수 있는 것이면 하면 안된다.
  • 유효성 검사 등을 이용해서 예방할 수 있는 것은 하자.

except 뒤에 추가할 수 있는 절

  • else 절
    - 예외가 발생하지 않고 정상적으로 수행된 경우 수행할 내용
    - 정상적인 처리 다음에 수행할 내용을 작성
  • finally 절
    -
    예외 발생 여부에 상관 없이 수행할 내용 작성
    - 이 절에는 외부 자원에 대한 연결을 해제하는 문장을 주로 작성
  • else와 finally를 같이 사용하고자 하는 경우에는 else를 먼저 작성해야 합니다.
  • finally는 예외 처리 구문이 종료됨을 의미합니다.
except ValueError as e:
    print("잘못된 데이터 입력!")
    print(e)
except ZeroDivisionError as e:
    print("0으로는 나눌 수 없습니다!")
    print(e)
#예외는 value 에러와 zerodivision 에러가 있는데
#한 곳에서 처리하고 있다.
else :
    print("예외가 발생하지 않은 경우 수행할 내용")
finally : 
    print("무조건 수행하는 문장 ")
    #finally 나오면 else 안나옴

근데 보통은 else 잘 안쓰긴 합니다.

강제로 예외를 발생시키기

  • raise Exception(메시지): 현재 함수 내에서 예외를 처리합니다.
  • raise : 호출하는 함수로 예외를 던지기
    while i<j:
        if num==1:
            raise ValueError("강제 예외 발생")

Assertion - 단언

  • 강제로 프로그램을 중단하는 것
  • assert 프록램이 중단되지 않는 조건[, 에러 메시지])
  • 예시 : DDoS

#num의 값이 2라면 메시지를 출력하고 프로그램 중단합니다.
assert num!=2, "num은 2이면 안됩니다."

✔ File Handling

파일 처리 과정

  • open이라는 함수를 이용해서 운영체제에게 파일을 사용할 수 있도록 요청
    - 파일이 존재하면 파일에 대한 참조를 리턴
    - 파일이 존재하지 않으면 생성을 하던가 예외를 발생시킴
  • 파일에 대한 읽고 쓰기 작업을 함수를 이용해서 수행하면 운영체제가 작업을 수행하고 그 결과를 리턴함
  • close 함수를 이용해서 파일을 닫기

파일 쓰기 작업

  • open 함수에 2개나 3개의 매개변수를 대입해서 파일을 열기
  • 첫 번째 매개변수는 파일의 경로, 두 번째는 'w', 세 번째는 인코딩 방식(생략하면 시스템 인코딩-utf8)
  • 기록을 할 때는 파일 참조 변수를 이용해서 write를 호출
  • \n이 포함된 컬렉션의 경우는 writelinesjoin을 이용해서 줄 단위 기록이 가능

파일 읽기 작업

  • open 함수에 1개나 2개의 매개변수를 대입해서 파일을 열기
  • 첫 번째 매개변수는 파일 경로
  • 두 번째 매개변수로 'r'을 대입할 수 있으며, 생략하면 'r'임
  • read()는 전체를 읽고, readline()는 줄단위로 읽고, readlines()는 줄 단위로 읽어서 list로 리턴하며, read(정수)는 정수 바이트 만큼 읽어옵니다.

현재 작업 디렉토리 확인 및 변경

  • os.getcwd() : 현재 작업 디렉토리 리턴
  • os.chdir('디렉토리') : 현재 작업 디렉토리 변경
import os
#상대경로를 설정할 때, 기준 경로가 됩니다.
#print(os.getcwd())
try:
    file=open('./docs/test.txt',encoding="utf-8")
#     file.write("utf-8로 인코딩했어요\n")#문자열 기록
#     lines=["mino","mino2","mino3"]
#     file.writelines(lines)
#     content=file.read() #전체 읽기
#     print(content)
#     #줄 단위로 읽어보자.
#     for line in file:
#         print(line)
#         print()
except Exception as e:
    print("파일 처리 중 예외 발생", e)
finally:
    file.close()

파일을 열어서 깨지면, 인코딩 작업을 해준다.
help(open) 하면 어디에 어떻게 쓸지 확인할 수 있다.

with 절

  • 파일은 외부 자원이라서 열어서 사용하고 작업이 끝나면 close를 호출해서 해제를 해야 합니다.
  • close 구문은 finally에 작성해서 예외 발생 여부에 상관없이 해제를 해야 합니다.

with open() as 파일변수:
위와 같이 작성하게 되면 open의 return 값을 파일 변수에 저장하고, with 절이 끝나면 예외 발생 여부에 상관 없이 자동으로 close를 호출합니다.

with open('./docs/test.txt',encoding='utf-8') as file:
    for line in file:
        print(line)
        print()

CSV 파일

텍스트 파일

  • 문자열을 기록한 파일
  • 텍스트 파일을 데이터를 저장하는 용도로 사용이 가능
  • fwt(fwf)
    - 일정한 간격을 갖는 텍스트
  • tsv
    - 탭으로 구분한 텍스트
  • csv
    - 쉼표로 구분한 텍스트
  • 최근에는 구분자로 구분된 것은 모두 csv라고 합니다.
  • 불변의 데이터인 경우는 csv로 제공하는 것이 일반적입니다.

CSV 읽고 쓰기

  • 텍스트로 읽어서 split 같은 메서드를 이용
    - 문자열 내에 따옴표가 있는 경우 해석이 안되는 경우가 발생
  • python이 제공하는 csv 모듈을 이용
  • pandas와 같은 외부 라이브러리를 이용

python이 제공하는 csv모듈을 이용하는 방법

  • 파일 객체를 csv.reader 객체에 대입하면 줄 단위로 읽을 수 있는 reader객체를 리턴하는데, 이 객체를 for로 순회하면 각 줄을 list로 변환해서 접근할 수 있도록 해줍니다.
  • 읽거나 기록을 할 때,
import csv
#텍스트 파일을 읽어서 list로 변환
#마지막 데이터에 \n이 추가됩니다.
#이를 제거해야 합니다.
data=[]
with open('./docs/한국주택금융공사_채권계산서 출력이력
_20201030.csv') as file:
    for line in file:
#         ar=line.split(",")
#         ar = [item.strip() for item in ar] #줄바꿈 문자 제거
#         data.append(ar)
줄 단위로 읽어서 list를 만들어주는 reader 객체를 생성    
    rdr=csv.reader(file)
    for line in rdr:
        data.append(line)
print(data)
  • csv.writer에 파일 객체를 대입하면 csv에 기록이 가능한 객체를 생성할 수 있는데, 이 객체를 가지고 wirterow(list)를 호출하면 csv파일에 기록도 가능합니다.

  • w 모드로 열면 기존 내용을 모두 지우고, a모드로 열면 기존 내용 뒤에 추가를 합니다.

import csv
data=[]
with open('./docs/한국주택금융공사_채권계산서 출력이력
_20201030.csv','a') as file:
    #쓰기모드 하려면 'w' 하지만 이는 지우고 함
    #그러기에 'a'를 사용한다. a는 추가하는 것이다.
    #쓰기
    wr=scv.writer(file)
    wr.writerrow([데이터 입력])x`
    wr.writerrow([데이터 입력])
print(data)

바이너리 파일과 Serializable

바이너리 파일

  • 바이트 단위로 파일에 기록하고 읽기가 가능함
  • 바이너리 파일로 만들 때는 문자열을 직접 기록할 수 없고, encode함수를 호출해서 바이트 단위로 기록
  • 바이너리 파일에서 읽은 데이터를 문자열로 변환할 때, decode함수를 호출해야 합니다.
  • 모드를 설정할 때, b를 추가합니다. (wb, rb)
  • 실제로는 텍스트보다는 이미지 등을 저장할 때 사용
data=[]
with open('./docs/test.bin','wb') as file : 
    #바이너리라 인코딩 필요없음
    file.write("안녕하세요".encode())
with open('./docs/test.bin','rb') as file : 
    content=file.read()
    print(content)
   	print(content.decode())

저렇게 읽으면 b'\xec\x95\x88\xeb\x85\x95\xed\x95\x98\xec\x84\xb8\xec\x9a\x94' 이렇게 나온다.

Serializable

  • 인스턴스를 파일에 기록하고 읽거나 네트워크를 통해서 전송하기 위한 작업
  • 입출력은 기본적으로 byte 단위나 character단위로만 가능한데, 사용자 정의 클래스의 인스턴스 단위로 가능하도록 하는 작업입니다.
    - 이 작업을 하는 경우는 응용 프로그램을 만들고 여기서 생성된 데이터를 다른 곳에서 함부로 읽지 못하도록 하기 위해서 입니다.
  • 대표적인 케이스 : 블랙박스
기록
  • pickle.dump(출력할 객체, 파일객체)
읽기
  • pickle.load(파일객체) : 객체를 1개 읽기
  • pickle.loads(파일 객체) : 객체를 전부 읽기
class DTO:
    def __init__(self, num=0, name="이름 없음"):
        self.__num=num
        self.__name=name 
        #__붙이면 private, 인스턴스 접근불가
    @property #getter를 만들어보자.
    def num(self):
        return self.__num
    @property
    def name(self):
        return self.__name
    @num.setter #setter 만들어보자.
    def num(self,num):
        self.__num=num
    @name.setter
    def name(self,name):
        self.__name=name
    #instance를 print함수에 대입했을 때, 
    호출되는 메서드 - 오버라이딩
    #출력을 편리하게 하기 위해서 재정의 - 디버깅 목적
    def __str__(self):
        return str(self.__num)+" : "+str(self.__name)
dto1=DTO(1,"mino1")
dto2=DTO(2,"mino2")
data=[dto1,dto2]
import pickle
try:
    with open("./docs/data.dat","wb") as f:
        #f에 data를 Serializable(직렬화)
        pickle.dump(data,f)
except Exception as e:
    print(e)

그냥 열어보면 이렇게 나온다.
€? ]??main뵆DTO뵑?걫}??_DTOnum봌?_DTOname뵆mino1봴bh)걫}?hKh?mino2봴be.

try:
    with open("./docs/data.dat","rb") as f:
        result=pickle.load(f)
        for dto in result:
            print(dto)
except Exception as e:
    print(e)

result는
[<main.DTO object at 0x0000022A3C059AE0>, <main.DTO object at 0x0000022A3C05B6A0>]
이것을 deserializable하면
1 : mino1
2 : mino2

파일 압축

zip 파일 압축

  • zipfile 모듈의 ZipFile이라는 함수를 이용해서 인스턴스를 생성하고 파일 경로를 list로 만든 후 write함수에 대입하면 압축이 됩니다.
  • zipfile 해제는 extractall함수를 호출하면 됩니다.
import zipfile
#파일의 목록 생성
filelist=["./docs/data.dat","./docs/test.bin"]
#파일 목록을 순회하면서 압축
with zipfile.ZipFile('./docs/test.zip','w') as myzip:
    for temp in filelist:
        myzip.write(temp)
#압축 해제
zipfile.ZipFile("./docs/test.zip").extractall()

tar(리눅스 압축 파일 표준) 파일 압축

  • tarfile 모듈을 사용하면 된답니다.

✔ log 파일 읽기 (쿠팡문제)

Apache Tomcat의 로그 파일

  • 180.76.15.5 - - [15/Nov/2015:03:45:45 +0000] "GET / HTTP/1.1" 200 6812
  • 공백 9개로, 실제 항목은 10개입니다.
  • 첫 번째 항목 : 접속 ip
  • 아홉번째 항목 : 응답코드
    - 200 : 읽는데 성공
  • 열번째 항목 : 트래픽(전송량)

로그 파일 읽어서 확인할 것 (쿠팡 문제)
1. 성공한 개수
2. IP 별 접속 횟수
3. IP 별 트래픽 합계
생각할 것 : 그룹핑 -> dict , counter

from collections import Counter
ipCount={}
response200=0
ipTraffic={}
try: 
    with open('./docs/log.txt','r') as f:
        for line in f:
            ar=line.split(" ")
            ar=[itemp.strip() for itemp in ar]
            #응답 횟수 카운트하기
            if (ar[8]==str(200)):
                response200+=1
            #ar[0]=ip, ar[0]을 key로 해서 ar[0]의 값에 
            #1을 더하기   
            ipCount[ar[0]]=ipCount.get(ar[0],0)+1
            #ip별 트래픽 합계를 구하기, 마지막 항목이 트래픽임
            #합계를 구하려면 정수로 변환해야 합니다.
            #counter도 가능합니다.
            try:
                ipTraffic[ar[0]]=
                ipTraffic.get(ar[0],0)+int(ar[9])
                #그냥 예외처리 해버리면 끝나네..?
            except:
                pass
    for ip in ipCount:
        print(ip," : " ,ipCount[ip])
    for ip in ipTraffic:
        print(ip," : " ,ipTraffic[ip])
except Exception as e:
    print(e)
    print("에러")
finally :
    print("끝")

예외처리하니까 끝나네
중복된거 계산은 dict, counter만 기억해내자!!

상위 10개 뽑아보기
Counter가 좋아요

    counter=Counter(ipTraffic)
    print(counter.most_common(10))

끝났네?

✔ 데이터 베이스(다시 정리하자)

-grant revoke : 관리자 언어가 맞지만 밑에는 개발자 언어다.

  • 예약어 : select, from where(정해져있는것)
  • 데이터는 대소문자 구분함

✔ Docker

개발환경

  • sw를 개발하는 환경
  • 일반적으로
    - 소스코드를 작성함
    - 컴파일
    - 빌드
    - 여기까지 오면 실행 가능한 상태가 된다.
  • 이 실행 가능한 것을 운영 환경으로 보냄
    - 마이그레이션(이행)이라고 합니다.

운영환경

  • 개발환경과 운영환경 os가 다를수 있고, db의 위치가 서로 다를 수 있기 때문에 소스코드의 변경이 필요하다.
  • 그럼 컴파일, 빌드를 다시해야한다.
  • 이러다 보면 제대로 동작하지 않는 sw들이 생기기 마련이다.

그렇다면 개발환경과 운영환경을 맞추자.

  • 하지만 운영환경에 있는 컴퓨팅 자원을 넘기기 쉽지 않다.
  • 그렇다면 가상에서 해보자.

OS 가상화

  • 컴퓨터를 똑같은 것을 못쓰니 os라도 똑같은 것을 써보자.
    - os 가상화 (VMware)
    - os위에 os를 깔아버리니 너무 무겁다.
    - os가 필요한 것이 아니라 그 위에 있는 프로그램인데

컨테이너 가상화

  • os는 프로그램을 실행시킬 정도의 기능만 설치하자.
  • 이후 sw를 띄워서 사용하자
  • 훨씬 가볍다!
    - 이를 프로세스 격리기술을 사용해서 한 것이다.
    - 이는 원래 리눅스에 있는 기술인데 이를 sw화 했다.
    - 이게 Docker이다.
  • 도커는 리눅스를 먼저 설치한다.(프로그램 실행을 위한 최소기능만)
    - 도커가 실행되는 환경을 동일하게 만들어주네!
  • 이게 가능해야 클라우드가 가능하다.
  • python 도 도커에서 가능하지만 linux를 알아야 하기에, 안한거에용!
  • db는 왜?
    - db에서 직접 작업하는 것이 아니다.
    - 응용프로그램에서 작업하기에 리눅스를 알 필요가 없다.
  • 도커는 컨테이너 가상화이다.

CI 코드 작성시 빌드까지 자동화 되어 나오는 것
CD 클라우드 등 배포

  • Image : 프로그램
  • Container : 프로그램을 실행한 것

Docker 설치

  • windows는 linux 바로 사용 못해서 WSL 설치를 해주어야 합니다.

설치를 합니다.


  • Accept 합시다

    cmd에 쳐줍시다.

    이후 sign in 해서 docker desktop을 실행합니다.

dbeaver : 데이터베이스 접속 도구

설치를 합시다

Docker에 MySQL 설치

  • Image를 다운로드 받아서 Container로 생성해야 합니다.
  • Container 생성 명령을 실행할 때, 이미지가 존재하지 않으면 이미지를 다운로드 받아서 Container를 생성합니다.
  • Image 다운 : docker pull
  • Container 생성 : docker run

MySQL 컨테이너 생성명령

  • docker run --name 컨테이너이름 -dit -net=네트워크이름 -e MYSQL_ROOT_PASSWORD=관리자비밀번호 -e MYSQL_DATABASE=데이터베이스이름 -e MYSQL_USER=사용자이름 -e MYSQL_PASSWORD=사용자비밀번호 -p 호스트포트번호:컨테이너포트번호 mysql이미지이름 --character-setserver=문자인코딩 --collation-server=정렬순서 --defaultauthentication-plugin=인증방식
  • 내 db에는 특정 프로그램만 접속하도록 해야 한다. 그러기에 네트워크 이름을 만듭니다.
  • dit는 백그라운드에서?
  • mysql은 root, 오라클은 sys
  • mysql 큰 작업단위는 데이터베이스이다.
  • root는 관리자가 로컬에서만 접속이 가능해야 합니다.
  • 이외의 사람은 샘플 계정으로 접속해야합니다.
  • 호스트포트번호 : 컨테이너포트번호
    - mysql 기본적으로 3306포트 사용합니다.
    - 컨테이너포트번호는 mysql 포트 번호입니다.
    - 호스트포트번호는 외부 들어올때 포트번호(포트포워딩?)
  • 문자 인코딩 : 설정 안하면 utf-8이지만, 이모티콘은 쓸 수 없음(게시판 등 이모티콘 저장 안됨)utfmb4 : 이모티콘도 쓸거야
  • 인증방식 : mysql8.0되면서 인증방식을 바꿨습니다. 비밀번호를 암호화해서 인증받도록 해놨습니다. (비밀번호 인증 이중화)

  • 뚝딱!

Kubernetes

  • 도커의 이미지가 여러개면?
  • 이를 관리(orchestration) 하는 것이 쿠버네티스!

SaaS

  • 서비스를 만들어주고 클라우드에 올려서 서비스 쓰게 하고 돈받는거에요
  • 앱 마켓 같은거지
  • IaaS는 인프라 만들고 쓰라고 하는거
profile
밀가루 귀여워요

0개의 댓글