나만의 무기

3-way 라우팅

우선 어제 공개하기로 한, 3-way 라우팅을 마무리했다.

위와 같이 원래는 없던 최초 화면을 추가해줬다.

어플을 껐다켜도 로그인을 유지했을 때, 최초 라우팅 페이지가 없으면 무조건 다른 화면이 한 번 깜빡이게 된다. 아니면 최초에 빈 화면을 기준으로 initialRoute를 해줄수도 있지만, 이 경우에는 어플이 알 수 없는 이유로 먹통이 되는 경우를 방지할 수 없다. 따라서 이렇게 최초 화면을 추가해주고, 클릭에 따라 다음과 같이 필요한 페이지로 넘어가게 해주었다.

시각적인 효과를 위해 글씨가 800ms마다 깜빡이도록 해주었는데, 세월아 네월아 깜빡이면 너무 정신없을 수 있으니 딱 4번만 깜빡이게 하였다.

1. 로그인 정보가 없는 경우 -> 로그인

2. 로그인 되어 있지만 명함이 없는 경우 -> 명함생성

3. 로그인 되어 있고 명함도 있는 경우 -> 명함첩

안전장치 -> 더블탭

먹통이 되는 경우를 대비해, Double Tap을 하면 어느 경우에든 로그인 화면으로 가도록 하는 장치도 마련해두었다. 물론 이때는 자동로그인이 되어 있더라도 그 정보를 clear하기에 정상적인 로그인이 가능하다.

이는 성격 급한 대한민국 사람들의 특성을 고려한 것인데, 어플이 명함생성이나 명함첩으로 못 넘겨주더라도 탭을 빠르게 반복하다보면 로그인 화면으로 넘겨주는 것이다. 장시간 사용하지 않는 사용자의 경우 자동으로 로그아웃 시키는 어플이 많다보니 거기서 아이디어를 얻었다.

명함첩 로딩 로직 수정

현재의 명함첩은 실제 친구 관계를 기반으로 가져오지만, 순서가 다소 뒤죽박죽 나오는 경향이 있었다. BE 코드를 뜯어보니 우선 로컬 DB에 만들어놓은 Connection에서 가져오는 친구 목록은 친구가 된 순서인 (1,2,5,4,6) 대로 알맞게 가져와진다. 하지만 이후에 카드를 뽑아오는 과정에 문제가 있었다.
글씨가 작으니 코드로 옮겨보자면

sql: 'SELECT `id`, ... , `updatedAt` FROM `cards` AS `card` 
WHERE `card`.`user_id` IN (1, 2, 5, 4, 6);',

SQL문이 이렇게 바뀌기 때문이다. 그래서 카드는 정작 명함카드가 생성된 순서대로 목록이 구성되었다. 명함생성을 빨리 한 유저부터, 즉 대체로 회원가입을 빨리 한 유저부터 명함첩에 뜨게 되는 것이다.

찾아보니 (1,2,5,4,6) 순서를 반영해서 뽑게 하려면 SQL에서는 FIELD 라는 것을 사용해서 정렬해야 한다.

SELECT ... FROM cards ORDER BY FIELD(user_id, 1,2,5,4,6)

Node.js에서 FIELD문을 쓰는 것 자체도 문제지만, 무엇보다 친구의 수가 사용자마다 다르다는 것이 가장 큰 문제였다. 시간을 약간 투자하면 할 순 있겠지만, 더 쉽고 효과적인 방법이 있다. 기존에 카드만 넘겨주던 API를 수정해서, Connection에서 뽑아온 알맞은 순서의 친구 목록을 카드들과 함께 넘겨줄 수 있도록 바꿔주는 것이다. 이렇게 하면 기존과 같은 방식으로 카드를 가져오기만 하면 되므로 서버에 추가적인 부담을 주지 않게 된다.

// Node.js
// card.service.js

  router.get('/all/:user_id', async (req, res) => {
    const { user_id } = req.params;
    const ids = await connections.findAllFriendsId(user_id);
    if (!ids.length) {
      res.send('no friend');
    } else {
      const allCards = await cards.findCards(ids);
      res.send({ cards: allCards, friends: ids });
    }
  });

그리고 플러터에서는, 여기서 보내준 친구 목록을 기준으로 리스트를 만들고 카드를 기준으로 카드 MAP을 만든다. 그리고 리스트에 따라 알맞은 순서대로 Map에서 인덱싱해서 ListView.seperate에서 출력하도록 했다. 특이한 점은 reverse를 시킨 것인데, 그 이유는 가장 최근에 친구가 된 사람의 명함부터 위에 보여주기 위함이다. 이렇게 해야 TOOK을 통해 공유한 후에 사용자의 명함이 다른 상대방의 명함첩 가장 위로 오게 될 것이다.

// Flutter
getAllCards(id) async {
    try {
      var dio = Dio();
      Response response =
          await dio.get('http://34.64.217.3:3000/api/card/all/$id');

      if (response.statusCode == 200) {
        final json = response.data;
        
        // 알맞은 순서로 넘어온 친구목록을 역순으로 구성해, 최근 친구부터 나오도록 만듬
        setState(() {
          friends = List.from(json['friends'].reversed);  
        });
        
        // 카드는 카드대로 Map 형태로 구성
        json['cards'].forEach((e) {
          var friendId = e['user_id'];
          setState(() {
            friendsData[friendId] = User(
              imagePath: e['image'],
              ....

이렇게 하면 (1,2,5,4,6)의 역순인 (6,4,5,2,1) 순서대로 명함첩 리스트가 알맞게 구성할 수 있다 😎 간단히 해결 !

알고리즘 스터디

구름 - 주유소

오늘 풀어본 문제는 주유소 라는 문제였다. 보다시피 일단 문제 길이가 상당하고, 숫자 범위도 엄청나다.
그냥 풀면 절대로 안 풀리겠다. 최소 O(N)...

그래서 이번에는 일단 코드를 치지않고 곰곰이 생각해보기 시작했다.
결국 비용이 최소가 되려면, 현재 도시를 지나는 시점에 지금까지 지나온 도시 중에 가장 저렴한 주유소에서 기름을 샀어야 한다는 결론이 나온다. 이걸 한 번에 1~N 범위에서 생각하려면 머리가 좀 아프지만, 일단 좁은 범위에서 생각해보기로 했다.

따라서 딱 4개의 도시만 놓고 생각해봤다.

우선 도시#1 에서 도시#2로 갈 때는, 선택의 여지가 없다. 그냥 무조건 도시#1의 기름값을 가지고 기름을 사야한다. 하지만 도시#2에서 도시#3으로 갈 때는, 만약 도시#2의 기름값이 더 저렴하다면 도시#2에서 살 수 있는 옵션이 생긴다. 대신 도시#2의 기름값이 오히려 비싸다면, 그냥 도시#1에서 기름을 추가로 구매한셈 치고 도시 #3으로 갈 수 있다.

일단은 도시#1의 기름값이 더 싸서, 도시#1의 가격으로 도시#3까지 왔다고 가정하자. 도시#3에서 도시#4로 갈 때는, 도시#1의 기름값과 도시#3의 기름값을 비교한다. 만약 도시#3의 기름값이 더 저렴하다면...... (위와 동일)

이렇게 같은 로직이 반복될 것이라는 것을 파악할 수 있었다. 이를 코드로 옮긴 결과는 다음과 같다.

N = int(input())
dist = list(map(int, input().strip().split()))
cost = list(map(int, input().strip().split()))

# [현재까지의 최소기름값, 현재까지의 누적비용]

DP = [cost[0], 0]
for i in range(1,N):
	DP[1] = dist[i-1] * DP[0] + DP[1]
	DP[0] = min(cost[i], DP[0])
	
print(DP[1])

사실 DP 문제는 아니었긴 한데, 제출한 코드를 그대로 가져왔다.
결국 기름값과 비용 2가지만 가지고 업데이트하면 되니까, 굳이 리스트를 안쓰고 변수를 2개 썼어도 됐을 것 같다.
한 번에 통과 완료! 오늘도 15분정도 걸렸다. 이번 주는 알고리즘 컨디션이 다시 좋네 :)
내일은 드디어 토요일! 내일까지 열심히 화이팅해야겠다. 아자아자 😎

0개의 댓글