6월, 2020의 게시물 표시

[WEB] css - flex

이미지
css - flex 기본적으로 css를 사용해서 element를 움직이는 방법에 대해 공부하고 정리 해본다. Horizontal div를 여러개 써 주었을 때 옆에 생기지 않는다. 바로 옆에 써 주었는데도 아래에 붙는다. 그 이유가 궁금해서 찾보았다. div는 width와 height가 있는 block의 성질을 가지고 있기 때문에 할당 공간을 제외한 나머지 여백에는 엄청나게 큰 마진이 생기기 때문에 아래에 붙는다. 그래서 div의 위치를 바꿔줄 때, 즉 한줄로 배치하려고 하면 다음과 같은 설정을 해 주어야 한다. display: inline-block inline과 inline-block은 차이가 있다. inline은 width와 height의 설정을 가지지 않기 때문에 div에는 쓸 수 없다. 예를 들면 문자열은 inline 할 수 있다. 하지만 div는 block의 성질을 가지고 있기 때문에 inline-block 을 써줘야 한다. 하지만 이 경우에는 우리가 설정하지 않은 미세한 여백이 생겨버린다. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .box { color: white; width: 100px; height: 100px; background-color: blue; display: inline-block; }

[업무 자동화] 파이썬으로 뉴스 클리핑 메일 자동 발송하기 (1)

이미지
[업무 자동화] 파이썬으로 뉴스 클리핑 메일 자동 발송하기 (1) 회사에서 회사와 관련된 최신의 뉴스를 공유하고 싶은 요구사항이 있었다. 크게 일의 진행을 3단계로 나누었다. 뉴스의 수집 뉴스의 선별 뉴스의 발송 그 중에서도 이번 글에서는 자동 발송 에 관한 이야기를 해 보려 한다. 수제맛집 메일의 발송과정을 문의해 전달 받았다. 아래와 같은 작업이 진행되고 있었다. 순서대로, 손으로 하고 있었다. 직원들에게 보내고 싶은 뉴스를 검색해서 엑셀에 나열한다. 리스팅 한 뉴스들을 가지고 메일을 작성한다. 전날 다음날 오전에 예약 발송을 설정한다. 어느 부분을 자동화 할 수 있을 지 생각해 보았다. 수집 -> 크롤링이나 RSS를 가지고 엑셀을 만들 수 있어 보였다. 메일 작성 -> 수집된 데이터가 형태만 잘 갖추고 있다면, 이메일 템플릿 화 할 수 있어보였다. 스케쥴러를 이용하면, 매일 같은 날 반복해서 보낼 수 있어 보였다. 파이썬으로 메일 보내기(smtplib) 뉴스 클리핑이 어떤 내용이던 파이썬으로 보낼 수 있어야 한다. 여기서 알게된 재밋는 사실은 당연하지만? 메일을 html로 만들어서 보내면 그대로 적용된다는 것 이었다. 파이썬으로 메일을 보내려면, gmail에서 비밀번호 설정을 해야한다. 구글 어카운트 에서 보안(Security) 에 접속한다. 앱 비밀번호(App passwords)에서 Mail, OS 선택 후 GENERATE (앱 비밀번호는 2단계 인증이 사용 설정된 계정에서만 이용할 수 있다.) 이 비밀번호는 메일을 보낼 때 마다 써야하기 때문에 (한번만)저장 해 놓는다. 아래 코드를 참고하여 메일을 발송하면 완성! import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText # basic information sender = &quo

[Swift-30-Projects] 03. IOS 클론코딩 FacebookMe

이미지
[Swift-30-Projects]03. IOS 클론코딩 FacebookMe 테이블 뷰를 이용해서 화면을 구성해보자. 앱 뜯어보기 한 화면의 테이블 뷰이다. 상단에는 네비게이션 바가 있고 Facebook이라고 적혀있다. 5개의 섹션으로 이루어져 있다. 프로필셀로 이루어진 섹션, 여러 정보들을 보여주는 섹션, 즐겨찾기 섹션, 설정과 정책 섹션, 로그아웃 섹션. 페이지 이동 할 수 있는 부분에는 화살표가 있고, 로그아웃은 중앙에 정렬되어있다. 기능은 구현하지 않고 껍데기만 구현해본다. 네비게이션 바 상단 네비게이션 바를 띄운다. 이번 프로젝트는 스토리보드를 쓰지 않고 진행한다. 하나의 테이블 뷰 이기 때문에 스토리보드를 쓰는 것 보다는 테이블 뷰를 보기 좋게 짜는게 구조화 되어 이해하기 쉬울 것 같다. ** 스토리보드 없이 코딩하는 방법 **을 이용해서 화면을 하나 띄운다. 다만 주의해야 할 점은 네비게이션 바 위에 뷰를 하나 띄운다는 점이다. title을 설정해주고, navigationController?.navigationBar.barTintColor 로 네비게이션 바의 색을 지정해주면 끝! 테이블 구조 전체 섹션은 5개이다. 각 섹션별로는 1,7,1,3,1개의 로우가 있다. 몇 몇 개의 로우를 제외하면, disclosureIndicator가 있다. 테이블 셀은 눌리고 나면 선택해제가 바로 되어야 한다. 프로필 섹션 프로필의 셀 모양은 subtitle이다. 다양한 셀 모양 을 알아두자. 텍스트라벨과, 이미지, 디테일텍스트라벨을 입력해주면 완성 기능 섹션 여러가지 기능이 있는 섹션이다. 셀의 스타일을 subtitle로 만들어주고 이미지와 타이틀을 넣어준다. FAVORITE 헤더의 길이를 길게하기위해, 아무 내용도 없는 셀을 하나 추가해서 2배로 긴 헤더를 만들어 주었다. 설정 섹션 기능 섹션과 마찬가지로 이미지와, 내용을 넣어준다. 로그아웃 섹션 글의 색상을 red로 바꿔주

[swift] Xcode에서 Unit Test 연습하기

이미지
[swift] Xcode에서 Unit Test 연습하기 진행하고 있는 프로젝트를 리팩토링 하면서, 내가 한 리팩토링이 잘 되었는지는 둘째치고 이전에 기능들은 잘 되고 있는지에 대한 불안감이 생겼다. 원래 되고있던 기능이 뭐지 제대로 되었다는것이 어떤 것을 의미하지 테스트 코드의 작성을 통해 위 두 문제를 해결할 수 있다고 해서 정리하기 시작했다. 리소스 다운 원문 번역본 모든 내용을 다 정리하지도 해 보지도 않았다 앞으로 필요한 테스트를 위한 기본 몸풀기 정도만 해 보았다. 추후에 해당 아티클들의 뒷 부분이 필요한 때에 다시 해 보고 정리하기로 한다. 테스트를 위한 네비게이션이 있었다. 뭐하는데 쓰는 것일까 궁금했더 곳이다. Command(⌘)+6 으로 창을 열 수 있다. 왼쪽 아래의 + 버튼을 눌러 새로운 유닛테스트를 위해 New Unit Test Target… 을 눌러 생성한다. 처음에 만들면 4개의 메소드가 있다. setUpWithError() : 테스트를 위해 설정 tearDownWithError() : 테스트가 종료 된 후 삭제 testExample() -> 삭제 testPerformanceExample() -> 삭제 BullsEye 우리는 50으로 부터 랜덤으로 값을 생성하고, 슬라이더를 움직여 목표 값과 비슷한 값을 입력하는 게임을 테스트 하려한다. 프로젝트 설정 1 . @testable import BullsEye 를 입력해 테스트 코드 파일의 최상단에 프로젝트를 임포트 해주자. 2 . var sut: BullsEyeGame! 프로퍼티를 class BullsEyeGame 타입으로 만들어준다. setUpWithError super.setUp() sut = BullsEyeGame() sut.startNewGame() 인스턴스를 생성하고, startNewGame까지 실행한 상태로 설정한다. tearDownWithError sut = nil s

[IOS] 뷰 컨트롤러의 생명주기 - Life cycle of ViewController

이미지
[IOS] 뷰 컨트롤러의 생명주기 스토리 보드 없이 뷰 컨트롤러를 호출하면 viewDidload()에서 이런 저런 설정을 해준다. viewDidload 는 언제 호출되고, 이 다음과 이전에 호출되는 것이 또 있을까? 라는 궁금증을 가지고 검색을 했더니 viewController life-cycle이라는 키워드를 찾았다. Life Cycle 뷰 컨트롤러는 어떤 생명주기를 가지고 있을까? 1 init 2 loadView 3 viewDidLoad 4 viewWillAppear 5 viewDidAppear 6 viewWillDisappear 7 viewDidDisappear -> 4 viewWillAppear 8 viewDidUnload 이런 생명주기를 가진다. 가장 먼저 뷰 컨트롤러에 구현되어있던 viewDidLoad를 시작으로 살펴보면, 로드 -> 나타남 -> 사라짐 -> 언로드의 순서를 가진다. Did와 Will로 전후에 호출되는 함수를 구별한다. 예를 들면 viewDidLoad는 뷰가 로드 되었다. 그 다음에 viewWillAppear는 뷰가 나타날 것이다. 와 같이 이름만 보아도 언제 호출되는지 알 수 있도록 만들어놓았다. viewDidload 뷰 로드 완료 후 자동을 호출된다. 리소스의 초기화에 많이 사용된다. 뷰가 처음 만들어질 때 한 번만 실행된다. viewWillAppear 얼핏 보면 viewDidload와 같은 기능을 하는 것 처럼 보인다. 실제도 viewDidload 바로 다음에 호출된다. viewDidload 와의 차이점은 위의 생명주기의 viewDidDisappear -> viewWillAppear 에서 나타난다. 뷰1:메인페이지, 뷰2:상세페이지가 있다고 가정하자. 화면의 이동이 있을 때, 메인페이지에서 상세페이지로 갔다가 다시 메인으로 돌아오면 이미 그려진(viewDidload가 한번 호출 된) 메인페이지는 다시 viewDidload가 호출되

[swift] fatalerror에 대해 처음 알아보았다

이미지
[swift] fatalerror 코드를 리팩토링 하다 에러문구를 만났고, Fix를 누르니 다음과 같은 코드가 생겼다. 이미 init 함수는 있는데 무슨 용도일까 해서 찾아 보았다. required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override init(frame: CGRect) 은 view를 만들 때 사용된다. required init?(coder: NSCoder) 스토리 보드에 생성될 때 사용된다. fatalError는 언제 무슨 용도로 쓰는지 알아보자. 설명 은 다음과 같았다. Unconditionally prints a given message and stops execution. 무조건 출력하고 실행을 멈춘다? 정의는 다음과 같다. func fatalError(_ message: @autoclosure () -> String = String(), file: StaticString = #file, line: UInt = #line) -> Never Never을 반환한다. 이 의미는 호출이 되면 더 이상 앱이 진행되지 않는다. 즉 개발자가 인지 하지 못하는 상황에서 예외처리를 이 함수로 한다면, 실행 되었을 때 문제가 있는 부분을 알 수 있다. 다른 예를 하나 더 들어보자. 테이블 뷰를 쓸 때 자주 구현하는 메소드이다. override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? MyCustomCell else {

맥북 중고로 팔기위한 여정

이미지
맥북 중고로 팔기위한 여정 새로운 맥북을 사기위해 쓰던 맥북을 팔아야 했습니다. 중고로 팔아야 했기 때문에 중고나라를 이용했습니다. 먼저 필요한건 내가 가진 맥북의 년도와 크기 + 스펙들입니다. 이 맥에 관하여 > 시스템 리포트로 들어가서 하드웨어 전원 -> 배터리 사이클 메모리 저장장치 스펙을 적어줍니다. 모서리 깨짐, 화면상태들을 사진으로 찍어서 글을 올리면 완성! 가격은…눈치껏 검색해보고 내것을 중고로 산다면 이라는 생각으로 검색해서 정합니다. 다음단계는 내 맥북을 깨끗한 상태로 만들어서 구매자에게 파는 것! 아이튠즈를 로그아웃 해줍니다. 아이 클라우드를 로그아웃 해줍니다. 아이 메시지를 로그아웃 해줍니다. 복구 모드로 가서 새로 설치해줍니다… 복구 모드에 가서 macOS 재설치만 해주면 되는 줄 알고, 7시간을 기다렸습니다. (네트워크 복구모드는 정말 느리더군요...) 재부팅 후 응? 설치했던 프로그램들이 그대로 있었습니다. 다시 찾아보니 디스크 유틸리티에서 삭제를 해 주어야 합니다. 디스크 유틸리티에서 내 디스크를 선택하고, 지우기를 해 주었습니다. 다시 네트워크 복구…7시간을 진행하던 중 네트워크가 끊기면서 에러가 발생했습니다ㅠㅠ. 그 이후로 macOS를 설치하면 복구 서버에 있는 설치 정보가 손상되었습니다. 메시지의 반복. 결국 최후의 수단인 USB 재 설치를 선택해야했습니다. 필요한 재료는 다음과 같습니다. 16G USB와 catalina.app 파일(약 8Gb). USB는 서랍 구석을 뒤져서 찾았습니다. 문제는 catalina.app 파일. 공식적으로 app store에서 구할 수 있지만 다운 받는데만 5시간. 누군가는 미리 다운받아 공유하지 않았을까 하는 생각에 찾아보았더니 역시 있었습니다! 해당 블로그 그 다음은 USB를 가지고 부팅 디스크를 만드는 방법! 다음 블로그 를 참조했습니다. 부팅 디스크를 만들고, 부팅할 맥에 꽂은다음 Option

Xcode에서 storyBoard없이 개발하기 - 뷰 하나 띄우기

이미지
storyBoard없이 개발하기 UIViewController를 storyBoard없이 만들기 IOS를 개발하다보니, storyBoard를 가지고 개발하는 경우와 없이 개발하는 방법 두 가지가 혼재되어있어, 이번 기회에 storyBoard없이 개발하는 방법에 대해 공부해보려 한다. 스토리 보드 없이 코드만으로 화면을 구성하려면, 스토리 보드만 지우면 되는게 아닌 메인 인터페이스 설정 및 코드상에서 window에 대한 설정을, 진입점을 잡아줘야 한다. 환경 설정 스토리 보드 지우기 -> 실행 (Thread 1: Exception: "Could not find a storyboard named ‘Main’ in bundle NSBundle) Project -> General -> Main interface -> 공백입력 info.plist -> Application Scene Manifest -> Storyboard Name -> 줄 전체 삭제 -> 실행 (Thread 1: Exception: "Invalid parameter not satisfying:) iOS 13 이상만) iOS 13 이전에는 모든 장면 기능이 AppDelegate.swift 클래스에 있었으며 이제는 SceneDelegate.swift라는 별도의 클래스에 있습니다. SceneDelegate.swift 에서 프로젝트를 설정하겠습니다. 아래와 같은 코드를 SceneDelegate.swift 에서 작성(수정)합니다. func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return }

[Swift-30-Projects] 02. IOS 클론코딩 Stopwatch

이미지
[Swift-30-Projects] 02. IOS 클론코딩 Stopwatch Stopwatch 앱 뜯어보기 화면 절반은 뷰, 절반은 테이블 뷰 뷰의 절반은 디스플레이, 나머지는 제어 버튼 테이블 뷰는 기록 셀이 들어가야 함 기능 한 페이지 짜리 앱이다. 초기 상태 : [LAB, START], START 버튼을 누르면 [LAB, STOP] LAB은 기록. STOP 누르면 [RESET, START] 시작 버튼을 누르면 스톱워치가 가야한다. 랩을 누르면, 기록이 저장되어야한다. 화면 구성 storyBoard로 구성할 수 있는 만큼 함. 싱글 뷰를 하나 만들고 하위에 스탑워치를 위한 뷰, 시작 종료를 위한 버튼2개, 기록을 나타낼 테이블 뷰를 넣는다. 뷰에는 라벨을 2개 넣어 기본 경과 시간과, 랩간 차이를 보여주는 라벨을 넣는다. 조작 버튼은 누를 때 마다 스탑워치의 상태를 변경한다. 테이블 뷰를 하나 만들고, 테이블 뷰 안에는 랩 버튼을 누를 때마다 셀을 넣어준다. 버튼동작 [스타트, 랩, 리셋, 스탑 버튼] 다른 기능은 생각하지 않고 버튼의 동작과, 스탑워치의 진행 상태값만 고려하여 구현한다. 초기는 [LAB, START] 상태로 둔다. START -> [LAB, STOP] -> [RESET, START] RESET 버튼을 누르면 초기화 상태. LAB 버튼을 누르면, 테이블에 시간 기록. 시간 시작, 멈춤, 초기화 isPlaying 이 true인 상태 즉 기록이 되는 중 에만 시간이 가고 false인 상태에서는 멈춰야한다. 데이터를 위해 타이머 모델을 하나 만들어준다. 필요한 내용은 타이머객체와 시간카운터이다. 타이머는 문서 를 보고 만든다. 0.035 초마다 시계를 업데이트 해줄 것이고 계속 반복하기 때문 mainStopwatch.timer = Timer.scheduledTimer(timeInterval: 0.035, target: self, sel