이름이 명확하고, 의미 있는 주석, 일관된 형식으로 체계적으로 구성되어야 한다.chatGPT에게 물어보자.유닛 테스트를 작성해야 한다.클래스와 함수는 분명하게 하나의 일을 하도록 구현해야 한다. (하나의 기능)모듈화하거나 혹은 루프를 돌려서 중복을 피해야 한다.로깅을 잘해야 한다.운영 관점에서 매우 중요하다.사용자를 염두에 두고 설계한다. 1) DRY (Don't repeat yourself)
- 같은 코드를 여러 번 작성하고 있다면 내가 중복된 코드를 사용하고 있다는 `시그널`.
- 한 번 작성하여 필요한 곳에 `재사용할 수 있는 방법`을 찾아야 한다.
- 그러므로 반복된 작업이면 `함수`로, 동일한 일이 반복된다면 `루프`, 비슷한 클래스가 계속 생긴다면 `클래스 계승 구조`를 사용해 볼 것.
#DRY가 적용되지 않은 경우
f_temp = 55
k_temp = (f_temp - 32) * (5/9)
f_temp2 = 49
k_temp2 = (f_temp - 32) * (5/9)
#DRY를 적용한다면
def fahr_to_celsius(fahr):
celsius = (fahr-32) * (5/9)
return celsius
k_temp = fahr_to_celsius(55)
k_temp2 = fahr_to_celsius(49)
2) KISS (Keep It Simple, Stupid)
- 하나의 함수가 너무 많은 기능을 가지면 안 된다. 한 함수가 너무 길이가 길어지면 이를 다수의 함수로 나눌 것.
- 불필요한 복잡성을 피하고 코드를 가능한 단순하게 유지한다. 코드를 꼭 짧게 작성해야 한다는 것은 아니다. (예를 들어 `람다 함수`)
- 다른 사람들이 사용하기 쉽고 테스트가 쉬워지기 때문에 읽기 쉽고 이해하기 쉬운 코드를 작성하기 위한 노력이다.
#KISS가 적용되지 않은 경우
f = lambda x: 1 if x <= 1 else x * f(x-1)
#KISS가 적용된 경우
def fac(number:int)->int:
if number <= 1:
return 1
else:
return number*fac(number-1)
생존이 더 중요하기 때문에 기술 부채가 점점 커짐. 코드의 품질보다는 속도에 초점을 맞추게 된다. 그로 인해 기술 부채가 발생한다.코드/서비스 리팩토링을 통해 이 기술 부채를 갚아야 한다.테스트 범위를 늘려야 하고 CI/CD를 도입해야 한다.이름 명명 규칙을 정하고 진행예외가 존재한다. Single character name은 쓰지 않는 게 좋지만 counter를 쓰거나 iterators를 쓸 때는 글자 하나의 변수를 사용해도 된다.try/except의 에러를 잡을 때 exception을 e라는 변수로 사용한다.파일을 다룰 때 f 변수를 사용해도 된다. __로 시작하거나 끝나게 쓰면 안 된다. (파이썬의 경우예약 언어로 이해할 수 있음.)데이터 타입을 쓰지 않는다.i = i + 1 #i에 1 더하기 (이런 건 코드만 봐도 분명하게 알 수 있는 주석이기 때문에 1이 무슨 의미고 왜 1을 더하는지를 설명하는 게 의미 있는 주석이다.)자주 체크인 하는 것이 좋다. 유닛 테스트와 같이 요청하면 좋다.주석을 최대한 추가하고 무슨 이유에서 뭘 하려고 하는 것인지 설명한다.코딩 스타일에 대한 것보다는 코드 자체에 대해서 이야기하는 게 좋다. github모듈의 특정 기능 (함수) 테스트. 보통 하나의 함수를 테스트해서 제대로 된 결과값이 나오는지 확인한다.input을 주고 기대한 output이 나오는지 확인하는 것이며 가장 낮은 레벨의 기본 테스트.import unittest를 사용한다.다양한 함수(여러 모듈)를 하나의 차원에서 테스트한다. 부하를 줬을 때도 (트래픽 등을 생성) 제대로 모듈들이 동작하는가.웹 페이지 자체의 기능을 테스트하는 게 대세이다.퍼센트를 나타낸다.Test Coverage가 높을수록 시스템이 안정된다.유닛 테스트를 위해 unittest 라이브러리를 import한다.테스트 클래스는 항상 unittest.TestCase를 상속받는다.유닛 테스트할 함수에 input 값을 넣어 받은 output값과 예상된 output 값과 비교해 본다. (일치하면 테스트 통과)-m unittest test_avg.py로 호출하여 테스트.assertEqual(a, b)는 a, b가 일치하면 True 아니라면 False. assertTrue(a) a가 True면 성공, False면 실패.import unittest
import avg
class averageTestCase(unittest.TestCase): # 테스트 클래스는 항상 testcase를 상속받는다
def test_average(self):
answer = avg.compute_average([1, 2, 3, 4, 5])
self.assertEqual(answer, 3.0) # input을 넣었을 때 나온 output 값인 answer가 3.0(원하는 결과값)인지 확인
def test_empty_input_average(self):
answer = avg.compute_average([])
self.assertEqual(answer, None)
if __name__ == "__main__":
unittest.main()
Python을 이용해서 웹 브라우저를 조작할 수 있는 자동화 프레임워크.동적인 웹 페이지의 웹 크롤링을 위해 주로 사용한다.selenium을 사용하기 위해서는 먼저 selenium을 설치해 주어야 한다.%pip install selenium
WebDriver는 웹 브라우저를 제어할 수 있는 자동화 프레임워크.웹 브라우저와 연동을 하기 위해 필요하다.%pip install webdriver-manager
# selenium으로부터 webdriver 모듈을 불러옵니다.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
webdriver.Chrome은 현재 우리가 사용하고 있는 Chrome과 동일한 버전을 사용해 싱크를 맞추기 위해 사용한다.driver라는 객체에 넣어 주는 과정이다.Requests와 동일하게 get 을 사용하는데 driver 객체에 get(url) 함수를 사용한다.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("http://www.example.com")
.page_source를 호출하면 응답을 받은 HTML 코드를 확인할 수 있다.드라이버 객체가 소멸될 때까지 Chrome창이 켜진 채로 유지가 된다.with-as 구문을 통해 주어진 명령이 끝나면 driver 종료가 되도록 처리해 준다.#with 여기서 실행하는 것을 명령이 진행될 때까지는 as (변수명)에 해당하는 변수명으로 부르겠다는 선언
#with절 내부에서는 변수명으로만 호출해 주면 된다
#with절이 종료되면 driver도 자동 소멸된다
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
driver.get("http://www.example.com")
print(driver.page_source)
.find_element(by, target), 요소가 두 개 이상이라면 .find_elements(by, target) 메소드를 사용한다.#.find_element를 사용하기 위해서는 By를 import 해 주어야 한다
from selenium.webdriver.common.by import By
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
driver.get("http://www.example.com")
#하나의 요소만 추출할 때
print(driver.find_element(By.TAG_NAME, "p").text)
#둘 이상의 요소를 추출할 때
for element in driver.find_elements(By.TAG_NAME, "p"):
print(element.text)
암묵적 기다림지정해 둔 특정 요소에 대한 제약을 통한 기다림이다..implicit_wait(초)로 호출을 해 주며 지정해 준 초 동안 기다리거나 지정한 초보다 더 빨리 렌더링이 된다면 완전히 렌더링이 될 때까지 기다려 준다.명시적 기다림지정한 시간 동안 기다린다.WebDriverWait()를 사용해 주는데 이때 .until(조건)을 줘서 조건이 만족할 때까지라고 지정할 수도 있고 .until_not(조건)으로 해서 조건이 만족되지 않을 때까지로 지정해 줄 수도 있다.다양한 Action을 확인 가능하다.ActionChains을 통해 Action들을 연쇄적으로 수행할 수 있게 해 준다. 그 이후 Action들을 나열하고 마지막에 perform()으로 종료해 준다. button = driver.find_element(By.ID, "button") #find_element를 통해 버튼 요소를 찾아 주고
ActionChains(driver).click(button).perform() #ActionChains을 통해 취할 모션을 나열해 준다.
send_keys_to_element를 통해 특정 키를 입력한다. ActionChains을 통해 Action들을 연쇄적으로 수행할 수 있게 해 준다. 그 이후 Action들을 나열하고 마지막에 perform()으로 종료해 준다.Enter: 명령 모드 -> 입력 모드ESC: 입력 모드 -> 명령 모드M : Code Cell (python 코드 실행 가능) -> Markdown Cell (Markdown 문법 활용)Y : Markdown Cell -> Code CellA : 'Above' 상단의 새로운 Cell 추가B : 'Below' 아래의 새로운 Cell 추가dd : 현재 Cell 삭제ctrl/cmd + Enter: 현재 Cell 실행 (code cell: 파이썬 코드 실행, markdown cell: 특정한 형식을 줄글로 표현) 1.
.find_elements(by, target)사용 시 주의할 것
.find_elements(by, target)로 추출되는 값은 두 개 이상의 요소로리스트 형식이 된다.- 그렇기 때문에
driver.find_elements(By.TAG_NAME, "p").text이렇게 바로 호출을 해 버린 후AttributeError: 'list' object has no attribute 'text'- 만약 각 요소를
for문과 같은 반복문을 이용해서
2.
WebDriverWait의NameError
- 명시적 기다림에 대한 처리를 해 주는 실습 과정에서
NameError: name 'WebDriverWait' is not defined다음과 같은 오류가 발생했다.- 이 이유는
WebDriverWait역시selenium.webdriver.support.ui의 한 라이브러리도 따로import를 해 줄 필요가 있었다.- 다음과 같은
NameError가 발생한다면from selenium.webdriver.support.ui import WebDriverWait를 해 주었는지 확인해 보자.Explicit Wait를 위해서는WebDriverWait를import해 주어야 한다.