이번 프로젝트 과제는 계산기 앱을 만드는 것이다.
보통 iOS개발에는 storyBoard방식과 codeBase방식이 존재한다. 두 가지의 방식은 각각의 장점이 존재한다. 나는 보통 storyBoard를 활용해 앱을 만들어왔기에 이번 앱은 codeBase로 만들어보았다.
단계를 8단계를 나눠놓아 차분히 따라가면 앱을 완성할 수 있었다. 1~5단계는 앱의 UI를 구현하고 6~8단계는 로직을 구현하는 느낌이다.
Lv.1 | Lv.2 | Lv.3 | Lv.4 | Lv.5 |
---|---|---|---|---|
// Label 생성
static var label: UILabel = {
let label = UILabel()
label.text = result
label.textColor = .white
label.backgroundColor = .black
label.textAlignment = .right
label.font = .boldSystemFont(ofSize: 60)
return label
}()
Label를 위와 같이 생성하며, Label에 필요한 조건을 같이 적어주며 생성하는 모습을 볼 수 있다.
// NSLayoutConstraint를 사용한 오토레이아웃
NSLayoutConstraint.activate([
label.heightAnchor.constraint(equalToConstant: 100),
label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30),
label.topAnchor.constraint(equalTo: view.topAnchor, constant: 200)
])
// SnapKit을 사용한 오토레이아웃
ViewController.label.snp.makeConstraints {
$0.height.equalTo(100)
$0.leading.equalToSuperview().offset(30)
$0.trailing.equalToSuperview().offset(-30)
$0.top.equalToSuperview().offset(200)
}
물론 오토 레이아웃 또한 우리가 직접 설정해줘야한다. 이 부분은 애플에서 제공해주는 NSLayoutConstraint이 있고, 좀 더 간단하게 사용할 수 있게 SnapKit이라는 라이브러리를 사용할 수 있다. 위의 코드는 두 가지의 방식으로 작성한 코드이며 그냥 보기에도 코드의 길이도 사용하기에도 단순화를 한 모습을 볼 수 있다.
버튼을 생성하고 생성한 버튼을 하나의 horizontalStackView에 넣어서 구현하면 된다.
각각의 버튼을 생성해주고 생성된 버튼을 스택뷰안에 넣는 모습을 볼 수 있다.
버튼을 생성하면서 설정해주는 반복된 코드들이 많기에 따로 메서드를 만들어 코드를 좀 간결하게 만들었다.
아무래도 계산기에는 버튼이 많고 형태가 비슷하며 하는 동작이 비슷해 반복되는 코드가 많기에 버튼을 생성하는 과정에서 메서드를 이용하면 좋다.
숫자 버튼과 연산자나 다른 버튼의 색상이 다르기에 생성할 때 이부분을 체크해서 생성해줘야 했다. 그래서 색상이 다른 버튼들을 하나의 배열에 모아놓고 버튼을 생성시 해당하면 주황색으로 아니라면 기본값으로 설정한 회색으로 버튼이 생성되게 하였다.
layer.cornerRadius = 40
라는 코드를 버튼을 만들때 넣어주면 된다.앞에서 언급 했듯이 button을 만드는 메서드를 통해 button을 생성하면 되는데 스토리보드와 달리 어떤 메서드를 실행하게 될지 addTarget
이라는 메서드를 이용해 정의해주면 된다.
addTarget에 선택된 메서드는 @objc라는 키워드를 사용해서 정의되어야 한다. 아래에 코드에 보이는 tappedButton메서드에서 입력받은 값을 에러처리한 후 통과된 값을 Label에 띄워줄 result값에 넣어 준다. switch문을 이용해 입력된 버튼에 해당하는 작업을 수행하도록 했으며, 만약 입력시 값이 "0"이라면 "0"을 제거한 후 값을 입력받도록 구현했다.
그리고 아래 코드에 보이는 changeLabelValue메서드를 호출에 Label값을 변경해주도록 한다. UI의 변경은 항상 메인스레드에서 이루어져야 하기에 DispatchQueue.main.async
라는 코드를 추가해주어야 한다.
초기에는 ViewController 하나의 파일에 모든 코드를 다 넣어서 구현을 하고 있던 와중에 MVC패턴을 적용해보고 뷰를 구현하는 클래스들도 나누어 구현을 하고자 파일을 나누어서 작업을 하고 있었다.
문제점
: 계산을 해주는 메서드도 ViewController에 있었지만 따로 빼놓았더니 제대로 작동하지 않았다.
해결 방안
: 아래 코드와 같이 계산 메서드를 프로토콜로 선언한 후 Calculate클래스에서 채택하게 한 후 ViewController에서 계산을 시킬 수 있도록 위임을 하여 해결했다.
문제점
: 버튼을 눌렀지만 미리 addTarget에서 선택한 메서드를 찾지 못하는 문제
처음에 설계할 당시 button을 ViewController에서 만들었지만 파일을 분리해 button을 뷰만 구현하도록 분리해놓은 것으로 문제가 시작되었다. addTarget에서 주체가 되는 즉 작업 메서드가 호출되는 개체 지정된 작업 메시지에 응답하는 객체를 검색하고 해당 객체에 메시지를 전달하는 역할을 지정하는데 이 부분에서 ViewController를 self로 받아야하는 상황이 안되는 것이였다.
아래의 코드처럼 map으로 맵핑을 하도록 설계를 함으로써 String값만을 이용해 설정해주지만 각 버튼에 접근해서 target을 설정 해주지 못하는 상황이였다.
해결 방안
: 결론적으론 설계의 오류로 인해 다시 설계를 해야한다에 도달았다... 물론 ViewController에서 맵핑으로 감싼 뷰들을 하나하나 풀어서 target을 지정해줄 순 있다고 하지만 과연 이게 올바른 방식인가? 그래서 설계에 오류가 있다는 걸 알게되었다. 그래서 이걸 뜯어 고치는 것보단 새로 다시하는게 낫다고 판단했다. 그래서 파일을 분리하지않고 우선은 완성이 목표이기에 하나의 ViewController에 코드를 모아서 작성해 해결하였다.
항상 스토리보드를 사용해 구현해왔지만 이번 기회를 통해 코드베이스로 앱을 구현해보는 좋은 기회가 되었다. 두 가지 모두 장단점이 있지만, 협업을 통해 일을 할 때는 스토리보드는 swift언어로 변환되지 않기에 코드베이스를 자주 사용한다고 하고, 외부에서 의존성을 주입하기에도 코드베이스가 더 편하다고 한다.
이번에 새롭게 배운 내용을 적용해보려고 시도를 해보았지만 오히려 이전에 배웠던 의존성, 응집도, 결합도 등을 고려하지 않고 짧고 간결하고 클래스만 나누면 된다는 오류를 범하게 되었다. 내가 왜 이 객체를 분리하고 재사용성을 고려하고 코드를 짰는가에 대한 생각을 하지 않아 발생한 설계오류를 경험하게 되었다. 다시한번 의미있는 코드를 짜야겠다는 생각이 든 프로젝트였다.
굿 너너무 고생 많앗어여 래훈님~~~!~