강의노트 11. 객체지향 방식 Data analysis, 캐시(cache)와 프록시서버

|

패스트캠퍼스 컴퓨터공학 입문 수업을 듣고 중요한 내용을 정리했습니다. 개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.

OOP Data analysis

개요

  • class_A.bin : 딕셔너리 형태로 이름과 점수 데이터를 가진 바이너리파일
  • evaluate.py : 평균과 표준편차를 연산하는 Evaluate class를 포함하는 파일
  • datahandler.py : DataHandler class를 포함하는 파일
    • filename, clsname을 매개변수로 받아서,
    • 데이터를 원하는 딕셔너리 형태로 재조합
    • 점수 데이터만 리스트 형태로 재조합
    • 평균, 분산, 표준편차 연산
    • 종합 평가결과 도출
    • 최고 득점자, 최고 점수, 최저 득점자, 최저 점수 도출
  • main.py : DataHandler class에 실제로 매개변수를 넣어서 인스턴스를 생성후 테스트

class_A.bin

  • 딕셔너리 형태로 이름과 점수 데이터를 가진 바이너리파일
 {'greg': 95},
 {'john': 25},
 {'yang': 50},
 {'timothy': 15},
 {'melisa': 100},
 {'thor': 10},
 {'elen': 25},
 {'mark': 80},
 {'steve': 95},
 {'anna': 20}

evaluate.py

  • Evaluate class를 포함하는 파일
    • average method : 평균
    • variance method : 분산
from functools import reduce


class Evaluate:
    def average(self, scores):
        return reduce(lambda a, b: a + b, scores)/len(scores)

    def variance(self, scores, avrg):
        return reduce(lambda a, b: a + b, map(lambda s:(s-avrg)**2, scores))/len(scores)

# 테스트 코드를 꼭 작성
if __name__ == '__main__':
    e = Evaluate()  
    li = [100, 95, 90]
    result1 = e.average(li)
    print(result1) # 95.0
    result2 = e.variance(li, result1)
    print(result2) # 16.666666666666668

datahandler.py

  • DataHandler class를 포함하는 파일
    • evaluator 멤버변수 : Evaluate class의 인스턴스
    • GetRawdataInDic 클래스 메소드 : class_A.bin 부터 데이터를 가져와 딕셔너리 형식으로 재조합한다.
    • init 초기화 함수 : 인스턴스 변수 (rawdata, clsname, cache) 정의
    • get_scores 메소드 : rawdata 에서 점수만 가져와서 리스트에 담는다.
    • get_average 메소드 : self.cache 에서 average 키에 해당하는 값을 가져온다. 없으면 추가한다.
    • get_variance 메소드 : self.cache 에서 variance 키에 해당하는 값을 가져온다. 없으면 추가한다.
    • get_standard_deviation 메소드 : self.cache 에서 standard_deviation 키에 해당하는 값을 가져온다. 없으면 추가한다.
from evaluate import Evaluate
from functools import reduce
import pickle
import math

# 이런 류의 클래스에는 Handler, Manager, Controlloer 라는 말을 자주 사용한다.
class DataHandler:
    evaluator = Evaluate() # 클래스 변수 : 클래스의 모든 인스턴스들이 공유하는 변수 (연산기)
                           # 객체합성 (상속을 받지 않고 객체합성을 한다.)


    # class method : 전역함수처럼 사용
    @classmethod # 인스턴스 메소드로 정의해도 문제는 없다
    def GetRawdataInDic(cls, filename):
        rawdata = {}
        with open(filename, 'rb') as f:
            while 1:
                try:
                    data = pickle.load(f)
                except EOFError:
                    break
                rawdata.update(data)

        return rawdata

    def __init__(self, filename, clsname):
        self.rawdata = DataHandler.GetRawdataInDic(filename)
        self.clsname = clsname
        self.cache = {}         # 연산한 값을 저장해 두는 저장소
                                #(필요할 때 연산하되 이미 연산된 값이면 연산 없이 저장된 값을 반환)

    def get_scores(self): # cache기법 사용
        if 'scores' not in self.cache:
            self.cache['scores'] = list(self.rawdata.values())

        return self.cache.get('scores')

    def get_average(self): # cache 사용
        if 'average' not in self.cache:
            self.cache['average'] = self.evaluator.average(self.get_scores())

        return self.cache.get('average')

    def get_variance(self): # cache 사용
        if 'variance' not in self.cache:
            vari = round(self.evaluator.variance(self.get_scores(), self.get_average()), 1)
            self.cache['variance'] = vari

        return self.cache.get('variance')

    def get_standard_deviation(self):
        if 'standard_deviation' not in self.cache:
            std_dev = round(math.sqrt(self.get_variance()), 1)
            self.cache['standard_deviation'] = std_dev

        return self.cache.get('standard_deviation')

    def GetEvaluation(self):
        print('*' * 50)
        print("%s 반 성적 분석 결과" % self.clsname)
        print("{0}반의 평균은 {1}점이고 분산은 {2}이며,따라서 표준편차는{3}이다".\
              format(self.clsname, self.get_average(), self.get_variance()\
                     , self.get_standard_deviation()))
        print('*' * 50)
        print("%s 반 종합 평가" % self.clsname)
        print('*' * 50)
        self.evaluateClass()

    def evaluateClass(self):
        avrg = self.get_average()
        std_dev = self.get_standard_deviation()

        if avrg <50 and std_dev >20:
            print("성적이 너무 저조하고 학생들의 실력 차이가 너무 크다.")
        elif avrg > 50 and std_dev >20:
            print("성적은 평균이상이지만 학생들 실력 차이가 크다. 주의 요망!")
        elif avrg < 50 and std_dev <20:
            print("학생들간 실력차는 나지 않으나 성적이 너무 저조하다. 주의 요망!")
        elif avrg > 50 and std_dev <20:
            print("성적도 평균 이상이고 학생들의 실력차도 크지 않다.")

    def who_ist_highest(self):
        if 'highest' not in self.cache:
            self.cache['highest'] = reduce(lambda a, b: a if self.rawdata.get(a) > self.rawdata.get(b) else b, self.rawdata.keys())
        return self.cache.get('highest')

    def get_highest_score(self):
        return self.rawdata[self.who_ist_highest()]

    def who_is_lowest(self):
        if 'lowest' not in self.cache:
            self.cache['lowest'] = reduce(lambda a, b: a if self.rawdata.get(a) < self.rawdata.get(b) else b, self.rawdata.keys())
        return self.cache.get('lowest')

    def get_lowest_score(self):
        return self.rawdata[self.who_is_lowest()]


캐시(cache) 와 프록시서버(proxy server)

  • 참고
  • 컴퓨터 시스템의 성능을 향상시키기 위해 주로 CPU 칩 안에 포함되는 빠르고 작고 비싼 메모리이다. 프로그램에서 직접적으로 읽거나 쓸 수 없고 하드웨어의 메모리 관리 시스템이 내부적으로 제어한다. 대부분 프로그램은 한 번 사용한 데이터를 다시 사용할 가능성이 높고, 그 주변의 데이터도 곧 사용할 가능성이 높은 데이터 지역성을 가지고 있다. 데이터 지역성을 활용하여 캐시보다는 느리지만 용량이 큰 메인 메모리에 있는 데이터를 캐시 메모리에 불러와 두고, CPU가 필요한 데이터를 캐시에서 먼저 찾도록 하면 시스템 성능을 향상시킬 수 있다.
  • 캐시 메모리는 데이터 지역성(Locality)의 원리를 사용한다. 데이터 지역성은 대표적으로 시간 지역성(Temporal locality)과 공간 지역성(Spatial Locality)으로 나뉘는데, 시간 지역성이란 for나 while 같은 반복문에 사용하는 조건 변수처럼 한 번 참조된 데이터는 잠시 후에 또 참조될 가능성이 높다는 것이고, 공간 지역성이란 A[0], A[1]과 같은 데이터 배열에 연속으로 접근할 때 참조된 데이터 근처에 있는 데이터가 잠시 후에 사용될 가능성이 높다는 것이다.
  • 쉽게 예를 들자면 무지하게 지랄맞고 부지런한 상사가 2010년 재무결산 보고서를 가져오라고 했을 때, 무슨 일인지는 몰라도 또 가져오라고 할지도 모르니까 2010년 재무결산보고서를 일단 준비해 놓고, 2009년이나 2011년, 2012년 재무결산보고서도 가져오라고 할지 모르니까 그것도 준비해 놓는 식이다. 또 다른 예로는 캐시는 지갑이라고 생각하면 된다. 지갑 혹은 주머니가 없다면 우리가 현금이 필요할 때마다 매번 은행이나 ATM에 가야 할 것이다. 이는 당연히 매우 귀찮고 시간도 많이 걸린다. 하지만 우리가 현금을 지갑에 넣고 다님으로써 시간을 절약할 수 있다.
  • CPU가 데이터를 요청했을 때 캐시 메모리가 해당 데이터를 가지고 있다면 이를 캐시 히트라 부르고, 해당 데이터가 없어서 DRAM에서 가져와야 한다면 캐시 미스라 부른다. 캐시 미스 발생시의 처리 방법은 캐시 정책에 따라 다르며, 데이터를 읽어 오는 시점으로 사용하기도 한다.

프록시서버 (proxy server)

  • 프록시서버
  • 프록시 서버 는 클라이언트가 자신을 통해서 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해 주는 컴퓨터나 응용 프로그램을 가리킨다. 서버와 클라이언트 사이에서 중계기로서 대리로 통신을 수행하는 기능을 가리켜 ‘프록시’, 그 중계 기능을 하는 것을 프록시 서버라고 부른다. 프록시 서버는 프록시 서버에 요청된 내용들을 캐시를 이용하여 저장해 둔다. 이렇게 캐시를 해 두고 난 후에, 캐시 안에 있는 정보를 요구하는 요청에 대해서는 원격 서버에 접속하여 데이터를 가져올 필요가 없게 됨으로써 전송 시간을 절약할 수 있게 됨과 동시에 불필요하게 외부와의 연결을 하지 않아도 된다는 장점을 갖게 된다. 또한 외부와의 트래픽을 줄이게 됨으로써 네트워크 병목 현상을 방지하는 효과도 얻을 수 있게 된다.

목적

  • 프록시 서버의 사용 목적은 잠재적으로 다양하다:
  • 익명으로 컴퓨터를 유지 (주로 보안을 위하여)
  • 캐시를 사용하여 리소스로의 접근을 빠르게 하기 위해. 웹 프록시는 웹 서버로부터 웹 페이지를 캐시로 저장하는 데 흔히 쓰인다.
  • 네트워크 서비스나 콘텐츠로의 접근 정책을 적용하기 위해. (이를테면 원치 않는 사이트를 차단)
  • 사용률을 기록하고 검사하기 위해 (이를테면 회사는 인터넷 이용을 파악)
  • 보안 및 통제를 뚫고 나가기 위해
  • 바이러스 전파, 악성 루머 전파, 다른 정보들을 빼낼 목적으로
  • 역으로 IP추적을 당하지 않을 목적으로
  • 전달에 앞서 악성 코드를 목적으로 전달된 콘텐츠를 검사하기 위해
  • 밖으로 나가는 콘텐츠를 검사하기 위해 (데이터 유출 보호)
  • 지역 제한을 우회하기 위해