📌 문제

파이어베이스에서 페이스북 로그인을 구현할 때 공식문서를 참고하여 Info.plist에 값을 넣었음에도 자꾸 다음과 같은 오류가 발생하였다.

 

오류 메시지를 읽어보면 info.plist 파일의 URL scheme 부분에 값이 제대로 들어가지 않았다고 한다.

오류가 난 info.plist 파일은 다음과 같다.

info.plist

 

 📌 문제 해결

나의 문제점은 URLSchemes의 아래 값을 넣어주는 부분에 fb를 붙여주어서 그렇다.

이런식으로 앞에 fb를 붙이지 않고 숫자만 넣어준다면 해결될 것이다.

 📌 결과 화면

아래와 같이 테이블뷰 안에 컬렉션뷰를 중첩한 구조를 만들려고 한다.

 

 📌 화면 구상

먼저 전체적으로 테이블뷰를 넣고 테이블뷰셀 안에 컬렉션뷰를 넣어주는 식으로 작업했다.

 

왼쪽부터 TV를 넣어준 SB, TVC(안에 CV) xib, CVC xib이다.

 

 📌 코드 - TV 설정

TV가 있는 스토리보드와 연결한 뷰컨트롤러 파일을 열어준다.

이후 스토리보드의 TV를 IBOutlet으로 연결해준다.

@IBOutlet weak var postTV: UITableView!

 

그리고 TVC를 등록하고 TVC의 delegate와 dataSource를 self로 설정해준다.

나는 해당 코드를 함수로 묶어 viewDidLoad에 넣어주었다.

private func setPostTV() {
    postTV.register(UINib(nibName: "PostTVC", bundle: nil), forCellWithReuseIdentifier: "PostTVC")
    postTV.delegate = self
    postTV.dataSource = self
}

 

UITableViewDelegate와 UITableViewDataSource 각각 코드를 작성해준다.

// MARK: - UITableViewDelegate
extension CreateMainVC: UITableViewDelegate {
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        /// 만약 서버통신 후 받아온 각각의 게시글의 이미지 개수가 2개이면 iPhone 13 mini 기준 높이를 268, 3개이면 207로 설정해준다.
        let height = (postList[indexPath.row].images.count == 2) ? 268 : 207
        
        return height
    }
}

// MARK: - UITableViewDataSource
extension CreateMainVC: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        /// 총 TVC의 개수는 서버 통신 후 받아온 게시글의 총 개수
        return postList.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: PostTVC.className, for: indexPath) as? PostTVC else { return UITableViewCell() }
        
        /// 순서대로 데이터 넣어주기
        /// TVC에 작성해준 setData 함수를 활용한다. 이때 CV를 위한 이미지 리스트도 전달한다.
        cell.setData(postList[indexPath.row])
        
        return cell
    }
}

 

 📌 코드 - TVC 설정

TVC xib파일과 연결한 UITableViewCell 파일을 열어준다.

이후 xib의 CV를 IBOutlet으로 연결해준다.

@IBOutlet weak var imgCV: UICollectionView!

 

그리고 CVC를 등록하고 CVC의 delegate와 dataSource를 self로 설정해준다.

나는 해당 코드를 함수로 묶어 viewDidLoad에 넣어주었다.

private func setImgCV() {
    imgCV.register(UINib(nibName: "ImgCVC", bundle: nil), forCellWithReuseIdentifier: "ImgCVC")
    imgCV.delegate = self
    imgCV.dataSource = self
}

그리고 TVC를 등록하고 TVC의 delegate와 dataSource를 self로 설정해준다.

나는 해당 코드를 함수로 묶어 viewDidLoad에 넣어주었다.

 

마지막으로 UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout 각각 코드를 작성해준다.

TV에서 setData함수를 통해 넘겨준 데이터 중 imgList를 활용한다.

// MARK: - UICollectionViewDelegate
extension PostTVC: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        /// CVC 개수는 이미지 개수와 같다.
        return imgList.count
    }
}

// MARK: - UICollectionViewDataSource
extension PostTVC: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImgCVC.className, for: indexPath) as? ImgCVC else {
            return UICollectionViewCell()
        }
        /// CVC에 작성해준 setData 함수를 활용한다.
        cell.setData(imgList[indexPath.row])
        
        return cell
    }
}

// MARK: - UICollectionViewDelegateFlowLayout
extension PostTVC: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        /// 게시글 이미지 개수에 따라 너비와 높이를 정해준다.
        let cellWidth = (imgList.count == 2) ? 163 : 104
        let cellHeight = (imgList.count == 2) ? 170 : 109
        
        return CGSize(width: cellWidth, height: cellHeight)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        /// 게시글의 이미지 개수에 따라 인셋을 정해준다.
        if imgList.count == 2 {
            return UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
        } else {
            return UIEdgeInsets(top: 12, left: 18, bottom: 12, right: 18)
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        /// 게시글의 이미지 개수에 따라 CVC간 간격을 정해준다.
        let spacingSize = (imgList.count == 2) ? 17 : 14
        
        return CGFloat(spacingSize)
    }
}

 

 📌 필요한 기능

학교 선택 -> 해당 학교 이메일 뒷부분(예를 들어 @korea.ac.kr) 확인 -> 일치한다면 입력한 이메일로 메일 전송 -> 인증

이런 로직으로 학교 인증을 구현하였다.

firebase를 사용하면 이메일 인증도 쉽게 구현할 수 있다.

 

 📌 이메일 전송 코드

// 로그인 및 메일 전송
const sentEmail = await signInWithEmailAndPassword(firebaseAuth, email, password)
  .then(() => {
    sendEmailVerification(firebaseAuth.currentUser);
    return { err: false };
  })
  .catch((e) => {
    console.log(e);
    return { err: true, error: e };
  });

if (sentEmail.err) {
  return res
    .status(statusCode.INTERNAL_SERVER_ERROR)
    .send(util.fail(statusCode.INTERNAL_SERVER_ERROR, 
    		responseMessage.SEND_VERIFICATION_EMAIL_FAIL));
}

 

 📌 인증 여부 확인 코드

const userFirebase = await signInWithEmailAndPassword(firebaseAuth, email, password)
  .then((user) => user)
  .catch((e) => {
    console.log(e);
    return { err: true, error: e };
  });
  
  ...
  
const {
  user: { uid: firebaseId, emailVerified: isEmailVerified },
} = userFirebase;
// const firebaseId = userFirebase.user.uid;
// const isEmailVerified = userFirebase.user.emailVerified; 와 동일

if(!isEmailVerfied) {
	// 이메일 인증이 되지 않은 유저에 대한 처리
}

인증 여부는 로그인 정보의 emailVerifed를 가지고 처리해주었다.

 

 📌 이메일 전송에서 어려웠던 점

serverless 환경이 아니라 서버측에서 이메일 인증을 구현하였기 때문에 해당 유저가 로그인이 되어있는지 알 수 없다.

따라서 로그인을 시키고 이메일을 전송해야 한다.

 

signInWithEmailAndPassword로 로그인을 시키고 그 이후 sendEmailVerification을 사용하여 인증 메일을 보낸다.

 📌 문제

에셋을 가져와서 적용하고 빌드를 했을 경우 원래 이미지와 색상이 다르게 나오는 경우가 있다.

 

asset

왼쪽 이미지와 같이 원래 검정색 아이콘을 가지고 탭바를 구성해주었는데,

실제 빌드 해보면 오른쪽과 같이 선택된 탭인 경우는 default tint색인 blue가 적용되어서 나온다.

(왼쪽 이미지 아이콘이 회색인 이유는 선택되지 않은 탭이기 때문이다.)

 

 📌 해결 방법

Assets.xcassets에서 해당 에셋을 선택하고 오른쪽의 인스펙터 창에서 Render As를 Original Image로 설정해준다.

오른쪽 집 아이콘은 Render As를 Original Image로 변경한 것이고, 왼쪽의 이미지 아이콘은 Render As가 Default로 설정되어 있다.

 

 

+ 참고

만약 Asset의 Render As를 Original Image로 설정할 경우 tabBar Item에 tintColor를 변경해주어도 아무런 반응이 없다!

 📌 필요한 기능

인스타그램 클론코딩을 하면서 탭바를 구현하였는데 메인탭에 진입시에는 흰색 탭바 배경과 검정색의 아이콘들이 필요했고,

릴스탭을 누를 때는 탭바 배경색은 black으로, item들 색은 white로 변경해야 했다.

구현 후 화면은 다음과 같다.

인스타그램 메인

 📌 초기 설정

* 스토리보드로 구현하였기 때문에 인스펙터 창에서 설정해주었습니다.

먼저 초기 설정을 위해 TabBarController가 있는 스토리보드에서 탭바를 선택하고 오른쪽 인스펙터 창에서 Background로 White Color로 설정하였다.

그리고 User Defined Runtime Attributes에서 unselectedItemTintColor와 tintColot를 Black으로 설정하였다.

* tintColor는 선택된 탭바아이템, unselectedItemTintColor는 나머지 탭바아이템 색상이다.

 

이렇게 하면 메인화면으로 진입 시 아래와 같이 나온다.

 

 📌 Delegate

릴스탭을 선택할 경우 색상들을 바꿔주기 위해 Delegate를 사용했다.

코드를 먼저 보고 설명하도록 하겠다.

import UIKit

class TabBarController: UITabBarController {
    
    // MARK: - Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setDelegate()
    }
}

// MARK: - Custom Methods
extension TabBarController {
    private func setDelegate() {
        self.delegate = self
    }
}

// MARK: - UITabBarControllerDelegate
extension TabBarController: UITabBarControllerDelegate {
    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        if selectedIndex == 2 {
            tabBar.backgroundColor = .black
            tabBar.tintColor = .white
            tabBar.unselectedItemTintColor = .white
        } else {
            tabBar.backgroundColor = .white
            tabBar.tintColor = .black
            tabBar.unselectedItemTintColor = .black
        }
    }
}

먼저 UITabBarControllerDelegate 프로토콜 채택 작업을 진행한다. 아래 부분에 해당한다.

extension TabBarController: UITabBarControllerDelegate {}

* 저는 가독성을 위해 extension으로 빼준 것이므로 아래와 같이 작성해주는 것도 가능합니다.

class TabBarController: UITabBarController, UITabBarControllerDelegate {}

이후 Delegate 대리자를 설정해주어야 한다.

self.delegate = self

이 코드를 viewDidLoad()에 작성해준다.

* 저는 함수로 빼주었습니다.

 

해당 코드는 탭바 아이템을 선택했을 때 TabBarController가 delegate를 사용하기 위한 것이다.

 

 📌 색상 변경

func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
    if selectedIndex == 2 {
        tabBar.backgroundColor = .black
        tabBar.tintColor = .white
        tabBar.unselectedItemTintColor = .white
    } else {
        tabBar.backgroundColor = .white
        tabBar.tintColor = .black
        tabBar.unselectedItemTintColor = .black
    }
}

tabBarController 함수는 tabBar delegate method로 탭이 선택되었을 때 작동한다.

 

selectedIndex는 몇 번째 뷰컨을 선택했는지에 대한 인덱스 값이다.

인덱스 값은 0부터 시작하므로 릴스탭은 인덱스 값이 2이다.

 

따라서 selectedIndex 값이 2일 경우 배경을 검정으로, 아이템을 흰색으로 바꿔주는 작업을 하면 된다.

추가로 selectedIndex 값이 2가 아닐 경우는 다시 배경을 흰색으로, 아이템을 검정색으로 바꿔주는 작업이 필요하다.

 

총 세 개의 속성을 주면 된다!

tabBar.backgroundColor = .black // 탭바의 배경 색상을 바꿔준다.
tabBar.tintColor = .white // 선택된 탭바 아이템의 색상을 바꿔준다.
tabBar.unselectedItemTintColor = .white // 선택되지 않은 탭바 아이템의 색상을 바꿔준다.

 📌 들어가며

비밀번호 입력창에 눈 아이콘 버튼을 두고 이 버튼을 선택함에 따라 비밀번호 보기/숨기기를 구현하려고 한다.

눈 아이콘 선택 시 비밀번호 보기/숨기기

 

해당 뷰는 스토리보드로 구현하였으며 해당 TextField와 Button 네이밍은 다음과 같다.

	@IBOutlet weak var passwordTextField: UITextField!
	@IBOutlet weak var passwordEyeButton: UIButton!

 

passwordTextField는 .isSecureTextEntry속성 default 값을 true로 주었다.

 

 📌 필요한 로직

이 기능을 구현하기 위해서는 세 가지 설정이 필요하다.

  • 버튼 선택 상태 변경
  • 비밀번호 보기/숨기기
  • 눈 아이콘(버튼 이미지) 변경

 

 📌 코드

    @IBAction func passwordEyeButtonDidTap(_ sender: Any) {
        // 보안 설정 반전
        passwordTextField.isSecureTextEntry.toggle()
        // 버튼 선택 상태 반전
        passwordEyeButton.isSelected.toggle()
        // 버튼 선택 상태에 따른 눈 모양 이미지 변경
        let eyeImage = passwordEyeButton.isSelected ? "password shown eye icon" : "password hidden eye icon"
        passwordEyeButton.setImage(UIImage(named: eyeImage), for: .normal)
        // 버튼 선택된 경우 자동으로 들어가는 틴트 컬러를 투명으로 변경해줌
        passwordEyeButton.tintColor = .clear
    }

 

 📌 마치며

처음에 eyeButton.isSelected 값을 바꿔주는 코드를 작성하지 않아서 비밀번호 보안 설정은 바뀌지만 눈 모양 아이콘 변경이 되지 않았다.

passwordTextField 보안 설정을 toggle()로 변경해준 코드를 보고 passwordEyebButton도 바꿔줬다고 착각해서 삽질을 했다는 거~  🫠

눈을 제대로 뜨자 👀

 

 📌 들어가며

서버 시간이 미국 기준이기 때문에 한국 시간으로 변경하는 것이 필요했다.

이번 포스팅에서는 aws RDS timezone 변경 방법에 대해 다뤄보려고 한다.

 

변경 방법은 크게

파라미터 그룹 생성 -> 파라미터 그룹 설정 변경(timezone 변경) -> 해당 디비에 생성한 파라미터 그룹 설정

으로 진행된다.

 

 📌 파라미터 그룹 생성

먼저 aws에 접속한다.

https://aws.amazon.com/ko/console/

 

AWS Management Console

AWS Support 플랜은 AWS로 성공하는 데 도움이 되는 다양한 도구, 프로그램 및 전문 지식에 대한 액세스의 조합을 제공합니다.

aws.amazon.com

 

이후 RDS 서비스를 검색하고 들어가서 왼쪽의 메뉴들 중 [파라미터 그룹]에 들어간다.

이미 데이터베이스를 하나 생성했기 때문에 default로 본인의 데이터베이스에 맞는 파라미터 그룹이 생성되어 있을 것이다.

dafault는 변경이 불가능 하기 때문에 파라미터 그룹을 새로 생성해주어야 한다.

 

오른쪽 주황색 버튼 [파라미터 그룹 생성]을 클릭한다.

 

파라미터 그룹 패밀리에는 본인이 사용한 데이터 베이스를 넣는다.

나는 postgreSQL을 사용했기 때문에 postgres12로 넣어줬다.

어떤 값을 넣어야 하는지 헷갈린다면 앞에서 보았던 default 파라미터 그룹의 패밀리와 동일하게 선택하면 된다.

 

파라미터 유형은 DB Parameter Group 그대로 선택한다.

그룹 이름과 설명은 본인이 원하는 대로 입력하면 된다.

나는 timezone 변경을 위한 파라미터 그룹이었으므로 그룹 이름에 timezone이라고 입력해두었다.

 

이후 생성 버튼을 누른다.

 

timezone이라는 파라미터 그룹 생성 완료 👏

 

 📌 파라미터 설정 변경 (timezone 변경)

생성한 파라미터 그룹을 클릭해서 들어간다.

나는 timezone을 클릭했다.

이런 화면이 뜨게 되는데,

여기서 [파라미터 편집]을 눌러준다.

 

이렇게 화면이 바뀌었다면

파라미터 필터링에 timezone을 입력한다.

 

postgreSQL을 사용했기 때문에 여기서 timezone을 UTC에서 UTC+9로 변경 후 [변경 사항 저장]을 누른다.

 

데이터베이스마다 어떤 형식을 사용해야 하는지는 aws 공식 문서에 있습니다.

https://aws.amazon.com/ko/premiumsupport/knowledge-center/rds-change-time-zone/

 

Amazon RDS DB 인스턴스의 시간대 변경

Amazon Relational Database Service(Amazon RDS) DB 인스턴스의 시간대를 현지 시간대로 변경하려고 합니다. DB 인스턴스의 시간대를 변경하려면 어떻게 해야 합니까?

aws.amazon.com

 

 📌 디비에 새로운 파라미터 그룹 설정

데이터베이스 인스턴스에 새로 생성한 파라미터 그룹으로 변경하기 위해 왼쪽 메뉴의 [데이터베이스]로 들어간다.

타임존을 변경할 데이터 베이스를 선택한다.

 

오른쪽 상단의 [수정]을 클릭한다.

 

아래로 스크롤 후 [추가 구성] - [데이터베이스 옵션] - [DB 파라미터 그룹]을 default에서 timezone으로 변경해준다.

 

[수정 예약] 옵션을 [즉시 적용]으로 선택한 후 [DB 인스턴스 수정]을 누른다.

 

성공!

 

혹시 Datagrip에서 한국 시간으로 제대로 안나온다 하시는 분은 Datagrip을 껐다 켜보시면.. 될 겁니다...!

제가 그랬습니다 하하

📌 프로젝트 생성

Xcode를 다운로드하고 처음 키면

이런 화면이 반겨줄텐데요,
새로운 프로젝트 생성을 위해 Create a new Xcode project를 눌러줍니다.
 
또는
 

왼쪽 상단 File - New - Project를 누르거나 단축키인 Shift + command + N을 눌러도 생성이 가능합니다.
 
 
프로젝트를 생성한다면 아래와 같이 많은 선택지가 나옵니다.

Application을 하나씩 알아볼게요!
 

1. App
    - 뷰를 사용하여 앱을 개발할 때 쓰는 템플릿 

2. Document App
    - 데이터를 저장할 수 있는 문서 기반의 앱을 개발할 때 사용하는 템플릿

3. Game
   - 게임 앱을 개발할 때 사용하는 템플릿

4. Argumented Reality App
    - 증강현실(AR) 앱을 개발할 때 사용하는 템플릿

5. Swift Playgrounds App
    - 플레이그라운드 앱을 개발할 때 사용하는 템플릿

6. Sticker Pack App
    - 스티커 팩 앱을 개발할 때 사용하는 템플릿

7. iMessageApp
    - iMessageApp을 개발할 때 사용하는 템플릿

8. Safari Extension App
    - 사파리 브라우저 확장 앱을 개발할 때 사용하는 템플릿

 
이 많은 템플릿들 중에 일반적으로 가장 많이 사용하는 건 App입니다 !!!!
그럼 App을 선택한 후 프로젝트 생성을 계속 진행해보겠습니다.

제가 처음 프로젝트를 생성할 때 당황했던 부분인데요.
여기도 하나 씩 설명해보도록 하겠습니다.
 

1. Product Name
    - 앱의 고유번호인 Bundle Identifier을 만드는 요소로 사용됩니다.
    - 사용할 프로젝트 이름을 넣으면 됩니다! ex) 1st-Seminar, DooriBon, NadoSunbae

2. Team
    - 애플 개발자로 생성된 인증서를 선택하는 부분입니다.
    - 애플 개발자 사이트에서 발급 받은 인증서가 있다면 목적에 맞게 서명된 앱을 만들어 낼 수 있습니다.
    - 만약 애플 개발자 계정이 없다면 Personal Team인 개인 계정을 넣어주면 됩니다.

3. Organization Identifier
    - 프로젝트 운영 기관 또는 조식의 식별값을 입력합니다.
    - 대체로 소속 회사나 해당 프로젝트의 도메인에서 www를 뺀 나머지 역순으로 사용합니다.
    - 이 값은 Product Name과 함께 Bundle Identifier의 구성 요소가 됩니다!

4. Bundle Identifier
    - 앱의 고유 식별 번호입니다.
    - Organization Identifier + Product Name으로 구성됩니다.

5. Interface
    - Interface를 구현할 때 사용되는 방법을 선택합니다.
    - 지금은 StoryBoard 방식을 선택하도록 하겠습니다.

6. Language
    - 앱을 개발할 때 사용할 언어를 선택합니다.
    - 지금은 Swift를 선택하겠습니다.

7. Use Code Data
    - 기기 내부에 데이터를 저장하는 경우, 이를 지원하는 코어 데이터를 사용할 것인지를 묻는 옵션입니다.
    - 사용 여부에 따라 선택하면 됩니다.

8. Include Tests
    - 개발한 소프트웨어의 품질을 테스트한 결과를 확인할 수 있는 단위 테스트 기능과 UI 요소에 따른 테스트를 할 수 있습니다.
    - 필요 여부에 따라 선택하면 됩니다.

 

Next를 누른 후, 원하는 경로를 선택하고 Create를 누르면 프로젝트 생성이 완료됩니다!

무사히 프로젝트가 생성되었네요 👏
 

 📌 프로젝트 기초 구성

- Navigation Area
   : 파일 탐색 및 파일 추가, 이슈 등 여러 정보를 하는 영역

왼쪽부터

  • Project navigator
    - 작업중인 프로젝트 파일 구성을 보여준다.
    - 새로운 파일을 추가하거나 삭제, 수정이 가능하다.
  • Source Control navigator
    - 소스 파일 버전 관리를 위한 네비게이터이다.
    - 레포지토리라고 불리는 소스 관리 전용 공간을 생성했을 때만 사용 가능하다. (프로젝트 생성 시 하단에 있는 Create Git repository on my Mac 선택 또는 Source Control -> Create Git Repositories 선택)
  • Symbol navigator
    - 프로젝트에서 작성된 클래스나 구조체, 메소드 등의 심벌을 구조적으로 관리할 수 있는 네비게이터이다.
  • Find navigator
    - 프로젝트 내부 내용을 검색할 때 사용한다.
  • Issue navigator
    - 코딩 중 문법 오류나 문제가 생길 경우 경고나 오류 표시가 나타난다.
  • Test navigator
    - 프로젝트 테스트 목적으로 생성된 정보를 보여준다.
  • Debug navigator
    - 디바이스 혹은 시뮬레이션이 실행되면 각종 정보를 보여준다.
    - 디버깅 중에만 정보가 표시된다.
  • Breakpoint navigator
    - 실행 중간에 코드의 진행을 멈추고 메모리나 변수값 등을 확인할 때 사용한다.
    - 코드 내 원하는 위치에 브레이크 포인터를 삽입하고 해당 목록을 확인할 수 있다.
  • Report navigator
    - 빌드 결과나 실행 결과 등을 확인할 수 있다.
    - 필터와 함께 사용할 수 있다.

 

- Editor Area
   : 코드 작성, 화면 설계 등의 작업을 하는 영역

  • 하단의 폰 모양 버튼을 누르면 어떤 기종으로 작업을 할지 정할 수 있습니다.

얼마 전 제인초이님께서 스토리보드 작업 관련 조언을 주셨는데,
사이즈가 큰 기종으로 작업을 하고 작은 기종에서 잘리는 것 << 사이즈가 작은 기종으로 작업을 하고 큰 기종에서 여백이 남는 것
이기 때문에 mini로 작업을 하는 것을 추천한다고 하셨습니다 🫶🏻
 

- Inspector Area
   : 선택한 파일에 대한 정보를 나타내는 영역

왼쪽부터

  • File inspector
    - 선택된 파일에 대한 정보를 보여준다.
    - story board라면? 파일 정보, 빌드 대상 iOS 버전, 자동 레이아웃 옵션 등 설정하는 항목 표시
    - 화면 크기에 대응하는 앱, 국가별 언어 설정 처리 가능
  • History inspector
  • Quick Help inspector
  • Identity inspector
    - 인터페이스 빌더에 추가된 객체와 이를 구현한 클래스 사이의 연결, 객체의 ID 등 고유 정보 관리를 할 수 있다.
    - UI 객체를 소스 코드에서 참조하기 위한 식별 값을 정의할 수 있다.
    - 소스 코드와 화면 상의 객체를 연결할 수 있다.
  • Attributes inspector
    - 선택된 객체의 속성을 관리한다.
  • Size inspector
    - 선택된 객체의 외형에 대한 속성을 관리한다.
    - 주로 크기, 위치, 배치 방식 등을 다룰 수 있다.
  • Connections inspector
    - 인터페이스 빌더와 소스 코드 간의 연결 관계를 관리할 수 있다.

 

- Debug Area
   : 디버그 관련 내용 표시되는 영역

  • 에러 로그, 출력 로그 등 확인 가능합니다.

 

- Tool Bar
   : 핵심 버튼이 있는 영역

  • ▶️ 버튼을 누르면 빌드가 되고, 시뮬레이터가 작동됩니다.
  • 이미지 중간 부분의 'iPhone 11'을 누르면 시뮬레이터 기종을 선택할 수도 있습니다.
    (여기서 아이폰을 유선 연결해서 시뮬레이터로 설정할 수 도 있습니다.)

 

각 부분마다 이런 아이콘이 있을 겁니다!
이걸 눌러주면 해당 부분을 접었다 폈다 할 수 있습니다.
저도 14인치를 쓰고 있기 때문에.. 접어 놓고 쓰는 게 눈이 조금이나마 편하더라구요.. 하하
 
 
다음으로는 파일들에 대해 살펴보겠습니다.
 

1. *.swift

    - Swift 클래스 파일로 앱의 소스 코드를 구성하는 역할을 합니다.
    - AppDelegate, SceneDelegate 파일은 앱 전체의 생명 주기를 관리하는 클래스 파일입니다.
        * 생명주기에 대해서는 따로 포스팅하도록 하겠습니다!
    - ViewController 파일은 화면(뷰)에서 처리하는 작업들을 처리하기 위한 소스 코드를 담고 있는 클래스 파일입니다.
      storyboard에 가면 화면이 보일텐데요, 일반적으로 화면 개수만큼 ViewController가 필요합니다!
 

2. *.storyboard

    - 유저 인터페이스를 종합적으로 구현하는 역할을 합니다.
    - Main.storyboard 파일은 프로젝트 생성 시 기본으로 생성이 되고, 앱의 GUI를 담당합니다.
    - LaunchScreen.storyboard 파일은 앱을 실행하면 처음 나타나는 시작 화면의 인터페이스 설계를 담당합니다. (스플래시)
    - 화면 간의 연결을 보여줍니다.
    - View Controller와 View를 담는 공간입니다.
    - 처음 iOS 개발을 공부하게 되면 이 스토리 보드에서 화면 구현을 공부할 거예요!
 

잠깐, View Controller는 뭐고, View는 뭔데 ?!

간단하게 View Controller 안에 View가 있다고 생각하면 될 것 같습니다.

View Controller는 뷰의 계층 구조를 관리하는 것입니다.
뷰 컨트롤러에는 모든 콘텐츠를 포함하는 하나의 루트 뷰(Root View)가 있으며,
이 루트 뷰에는 콘텐츠를 표시하는 데에 필요한 뷰(View)를 추가합니다.

View는 iOS 앱 사용자 인터페이스를 구성하는 기본 구성 요소입니다.
뷰를 사용해서 화면에 컨텐츠를 배치하거나 그릴 수 있으며 뷰 안에 또 다른 뷰가 들어갈 수 있습니다.
UI 컴포넌트인 Label이나 Button 등도 하나의 View 입니다.


‼️ ViewController.swift 파일과 View Controller는 다릅니다!!!!!

위에서 swift 파일에 대해 언급을 했 듯이 ViewController 파일은 작업에 필요한 소스 코드를 담고 있는 파일입니다.
먼저 ViewController.swift 클래스 파일은 말 그대로 '클래스' 파일이기 때문에 storyboard에 있는 View Controller와 연결해서 사용하게 됩니다.
그럼 해당 View Controller에서 ViewController.swift 파일에 있는 소스 코드를 사용할 수 있겠죠?!

 

3. Assets.xcassets

    - 이미지 및 아이콘 등의 리소스를 관리할 때 사용하는 폴더입니다.
    - 슬라이더를 커스텀하거나 앱의 아이콘 등등이 포함될 수 있겠죠?!
 

4. info.plist 

    - 프로젝트 설정을 담당하는 파일입니다.
    - 시작화면 설정, 네트워크 접근 허용, 카메라 접근 허용, 위치 접근 허용 등이 있습니다.
 

 📌 마치며

기깔난 세미나 자료를 만들어 주신 솝트 30기 iOS 파트장 태끼님께 감사의 말씀을 전합니다...
 
 
참고
* SOPT 30th iOS 1차 세미나 자료
* https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=sqlpro&logNo=221045495843 
 

 📌 들어가며

저번 포스팅에서는 node.js와 Express를 이용한 간단한 서버 구축을 해보았습니다.

이번 글에서는 Express 폴더, 파일에 대해 좀 더 자세하게 다뤄보려고 합니다.

 

⬇️ 저번 포스팅 보러가기!

https://ju-hyeon.tistory.com/28

 

[Server] nodejs, express 서버 구축

 📌 들어가며 최근에 서버 프로젝트를 몇 개 시작하면서 서버 구축을 처음부터 다뤄보고자 글을 작성하려고 합니다. 이 글에서는 간단한 express 프로젝트 생성하는 법을 다루고, 다음 글에서는

ju-hyeon.tistory.com

 

 📌 Express 구조

​가장 상단의 practice (express 명령어로 만든 프로젝트의 이름) 를 눌러보면

5개의 폴더와 app.js, package.json을 볼 수 있습니다.

 

위에서 부터 하나씩 뜯어보도록 하겠습니다.

 

1. bin 폴더

bin폴더에는 오른 쪽 이미지와 같은 www파일이 있습니다.

이 파일은 서버를 실행하는 스크립트이며 프로젝트에 할당되는 포트 번호를 변경할 수 있습니다.

 

🔴 포트번호 주의 (겹치지 않도록 주의해야 합니다.)

1 ~ 1023: well-known port

1024 ~ 49151: registered port

49152 ~ 65535: dynamic port

 

well-known port란?

국제인터넷주소관리기구인 ICANN에서 TCP, UDP에 할당한 포트 번호입니다.

해당 포트 번호들은 특정 애플리케이션을 사용하기 위해 할당된 포트 번호입니다.

예를 들어, 20번과 21번 포트는 TCP, 22번은 SSH 등이 있습니다.

각 포트는 강제가 아니라 권고 사항이라 할당된 포트 번호를 그대로 사용하지 않고 다른 포트 번호를 사용하기도 합니다.

 

registered port란?

특정 용도로 사용하기 위한 포트 번호입니다.

등록된 포트라는 것은 말 그대로 기업에서 등록해서 사용이 가능한 포트입니다.

예를 들면, 8080번 포트는 HTTP 포트를 대체할 수 있는 포트 또는 MS에서 제공하는 SQL을 사용하기 위한 1433 포트 등이 있습니다.

registered port도 강제가 아닌 권고사항이라 할당된 포트 번호를 그대로 사용하지 않고 다른 포트 번호를 사용하기도 합니다.

 

dynamic port란?

특별히 용도가 지정되지 않았으며, 어느 프로그램에서나 사용이 가능한 포트 번호입니다.

 

포트 설명 출처: https://m.blog.naver.com/kyeong477/221558180792

 

 

2. public 폴더

public 폴더에는 각종 리소스들이 포함되어 있습니다.

이미지, JS파일, stylesheets 파일을 볼 수 있네요!

 

이 폴더는 서버에서 건드릴 일이 없습니다!

 

 

3. routes 폴더

route 폴더에는 페이지 라우팅과 관련된 파일들이 들어있습니다.

주소 별로 라우터들을 모아둔 것이며 URL 별로 실행되는 실제 서버 로직입니다.

 

index.js를 시작으로 관리해주면 됩니다.

이 라우터에 대해서는 제가 주로 사용하던 폴더링 구조에서 확인하시면 이해하기 쉬울 겁니다!

 

 

4. views 폴더

views 폴더에는 jade, ejs 등 템플릿 파일들을 모아 둡니다.

웹 서버 사용 시 해당 폴더의 파일들을 사용해서 렌더링 하는 것입니다.

 

이 폴더도 public 폴더와 같이 서버에서 건드릴 일이 없습니다!

 

 

5. app.js

app.js 파일은 프로젝트의 중심이자 핵심적인 서버 역할을 합니다.

이 파일에서는 미들웨어 관리가 이루어지며 라우팅의 시작점이 됩니다.

 

 

6. package.json

package.json 파일은 저번 포스팅에서 설명한 것과 같이 프로젝트에 사용된 모듈들이 나와있습니다.

 

npm install 명령어를 이용해 해당 파일에 있는 모듈들을 모두 설치할 수 있습니다.

또한 npm <모듈명>을 하게 된다면 package.json 파일에 추가됩니다.

 

 

 📌 마치며

Express처럼 자동으로 생성되거나 타인의 프로젝트 내용을 이해하기 위해서는 폴더, 파일을 뜯어보는 것이 정말 중요합니다!

저도 동아리에서 처음 서버를 공부할 때는 파트장님이 만들어주신 보일러 플레이트를 사용하여 api 코드를 짜는 것에 집중을 했었습니다.

최근에 진행한 서버 프로젝트에서는 정말 모든 파일을 뜯어본 것 같습니다.

시간이 좀 걸려도 이렇게 공부했던 것이 서버 로직 이해 뿐만 아니라 코드 짜는 것에도 도움이 되고, 오류가 나도 어디서 오류가 났고 어떤 것을 수정해야 하는지 알기 쉬웠던 것 같습니다.

api를 짜는 것도 중요하지만 파일을 처음부터 하나하나 뜯어보는 것을 추천드립니다!!!! 파이팅 👏

 

다음 포스팅에서는 firebase functions를 이용한 서버 초기 셋팅 방법, 자주 사용한 폴더링 구조를 공유하도록 하겠습니다.

 

 

 

참고: SOPT 29th 서버 파트 2차 세미나 자료

 📌 들어가며

최근에 서버 프로젝트를 몇 개 시작하면서 서버 구축을 처음부터 다뤄보고자 글을 작성하려고 합니다.

이 글에서는 간단한 express 프로젝트 생성하는 법을 다루고, 다음 글에서는 Express 구조 뜯어보기,

그 이후에는 firebase functions를 이용한 서버 프로젝트 및 자주 사용했던 폴더링 구조를 공유하도록 하겠습니다. 😃

 

 📌 사전 준비

1. Nodejs 설치

 

잠시, Nodejs란?

Node.js는 Chrome V8 Javascript 엔진으로 빌드된 Javascript 런타임 입니다

 

쉽게 말해서, javascript로 작성한 코드로 서버를 실행시키기 위한 도구다~ 라고 알고 계시면 될 것 같습니다.

 

https://nodejs.org/en/

 

Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

해당 링크로 들어가서 LTS 버전 다운로드 합니다.

 

설치 후 터미널에서

$ node -v
$ npm -v

두 가지의 명령어가 실행된다면, 정상적으로 설치 된 것!

 

2. VS Code 설치

https://code.visualstudio.com/

 

Visual Studio Code - Code Editing. Redefined

Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud applications.  Visual Studio Code is free and available on your favorite platform - Linux, macOS, and Windows.

code.visualstudio.com

해당 홈페이지에서 본인의 OS에 맞게 다운로드 합니다.

 

 

3. Express 설치

 

Express란...?

Node를 위한 빠르고 간결한 서버 프레임워크로
HTTP 요청에 대해 라우팅 및 미들웨어 기능을 제공한다.

$ sudo npm install express-g

* sudo 명령어는 max, linux 환경에서만 사용하면 됩니다 !!

 

-g 옵션을 주어 전역 설치를 하기 때문에 어느 경로에서 설치를 하든 상관 없습니다.

 

 

 📌 프로젝트 생성

1. VSCode 실행

2. Terminal을 사용해서 프로젝트를 만들 위치로 이동

VSCode를 실행했다면 mac 기준 ^(control) + ` 로 터미널 창을 열어줍니다.

만약, 열리지 않는다면 혹시 현재 언어가 영어가 맞는지 확인해주세요!

(영어 `가 한글로 하면 로 표시될 겁니다!)

 

터미널에서 아래의 명령어를 입력해줍니다.

$ npm install -g express-generator

-g는 --global 옵션의 줄임말로 전역 설치 옵션이며 express-generator는 express 프로젝트 생성기입니다.

 

설치가 된 것을 폴더에서 보고싶다! 하시는 분은

/usr/local/lib/node_modules

폴더로 이동하면 전역으로 설치한 모듈들이 보일 겁니다.

 

이제 터미널에 하단의 명령어를 입력해줍니다.

$ express practice

 

프로젝트 생성 완료!

 

 📌 Express 프로젝트 실행

프로젝트 최상위 폴더 위치로 이동합니다.

쉽게, 프로젝트 생성 시 적었던 express <프로젝트명> 에서 프로젝트명으로 이동해주면 됩니다.

 

해당 위치에서 dependency 설치를 위해 아래의 명령어를 입력합니다.

$ npm install

설치를 했다면 node_modules라는 폴더가 생겼을 겁니다.

해당 폴더는 dependency를 모두 담고 있는 폴더입니다.

 

npm install 명령어는 package.json에 있는 모듈들을 인스톨 해줍니다.

여기에 있는 dependencies를 인스톨해주는 것입니다!

 

이제 express 시작을 위해 아래 명령어를 입력해줍니다.

$ npm start

이 상태에서 브라우저에 접속해줍니다.

 

프로젝트 실행 완료!

 

다시 VSCode로 돌아와서 터미널을 보면

GET 요청이 실행되었던 것을 확인할 수 있네요!

 

 📌 마치며

이번 글에서는 간단히 Express 프로젝트 생성 & 실행을 다뤄보았습니다.

다음 글에서는 Express 프로젝트 생성 시 나오는 폴더 & 파일에 대해 더 자세히 알아보겠습니다!

 

참고: SOPT 29th 서버 2차 세미나 자료

+ Recent posts