728x90

https://school.programmers.co.kr/learn/courses/30/lessons/150368?language=javascript

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

function solution(users, emoticons) {
    let answer = [0, 0];

    function discountCombinations(discounts, numProducts) {
    
        function generateCombo(currentCombo, depth) {
            if (depth === numProducts) {
                let plus = 0, sell = 0; //플러스 가입자수, 총 판매금액 
                
                //reduce를 사용해 이모티콘 총 구매금액을 계산 
                for (let user of users) {
                    let total = emoticons.reduce((sum, price, c) => 
                        sum + (currentCombo[c] >= user[0] ? price * (1 - currentCombo                         [c] * 0.01) : 0), 0
                    );
                    //총 구매한 금액이 목표금액보다 비싼경우 구매를 취소하고 플러스 가입 
                    if (total >= user[1]) plus++;
                    else sell += total;
                }
                //그 경우의 수 마다 max값을 비교해서 최적의 결과를 answer에 넣어줌 
                if (plus > answer[0] || (plus === answer[0] && sell > answer[1])) {
                    answer = [plus, sell];
                }
                return;
            }
            //재귀
            for (let discount of discounts) {
                generateCombo([...currentCombo, discount], depth + 1);
            }
        }
        generateCombo([], 0);
        
    }

    discountCombinations([10, 20, 30, 40], emoticons.length);
    return answer;
}

 

재귀함수를 사용해서 각 이모티콘별로 할인되는 모든 경우의 수를 구하고, 그 경우의 수 중 max 값을 찾는 

무식한 방법을 사용했다 ㅎㅎ ... 

이모티콘의 갯수가 많지 않고 할인은 10 20 30 40 단 네가지의 경우의 수만 존재하기때문에 가능했던 풀이 

728x90

https://school.programmers.co.kr/learn/courses/30/lessons/148653#qna

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

탐욕Greedy 알고리즘을 사용해 풀어보자 

 

자신의 자리가 5 이상이라면 올림으로 처리하는것이 더 낫다 

e.g) 95인 경우에는 10*9 + 1*5 총 14회 보다 , 100*1 - 1*5 해서 총 6회로 나오는것이 더 적게 든다 

 

function solution(storey) {
    let ary = String(storey).split("").map(Number); 
    let carry = 0; //올림수
    let total = 0; 

    for (let i = ary.length - 1; i >= 0; i--) { 
        let current = ary[i] + carry;

        if (current > 5) {
            carry = 1;
            total += 10 - current;
        } else if (current === 5 && i > 0 && ary[i - 1] >= 5) {
            carry = 1;
            total += 5;
        } else {
           
            carry = 0;
            total += current;
        }
    }

  
    if (carry > 0) {
        total += carry;
    }

    return total;
}

 

555의 경우에는 올림을 해서 계산을 할 때와 올림을 적용하지 않았을때 둘 다 15회로 값이 동일하다 

728x90

 

https://school.programmers.co.kr/learn/courses/30/lessons/132265

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

function solution(topping) {
    var answer = 0;
    let chul = new Map();
    topping.forEach((f)=>{
        if(chul.get(f)) chul.set(f,chul.get(f)+1);
        else chul.set(f,1);
    })
   
    let dong = new Set();
    for(let i =0; i<topping.length; i++){
        dong.add(topping[i]);
        if(chul.get(topping[i])){
            if(chul.get(topping[i])>1) chul.set(topping[i],chul.get(topping[i])-1);
            else chul.delete(topping[i])
        }
        if(chul.size ==dong.size){
            
           answer++;
            
        }
    }
    return answer;
}

 

처음에는 양쪽 다 set을 써서 풀려고 했는데

생각해보니 양쪽 다 동일한 토핑을 갖게되는 경우도 있어서 한쪽은 map을 써야한다는걸 알게되었다... 

set과 map을 써봤으면 간단히 풀 수 있는 문제군 

728x90

https://react.dev/reference/react/useRef

 

useRef – React

The library for web and native user interfaces

react.dev

 

ref는 우리가 익히 알듯, 랜더링과 관계없는 값을 참조할 때 사용한다.

 

주로 js에서의 getElementById와같이 dom 요소에 직접 접근하고 싶을때 대신 사용하는것이 useRef다.

useRef를 통해 값을 변경하는 경우에는 화면이 재랜더링 되지 않으므로

 

보통 input의 focus 상태를 제어하거나, dom의 css를 직접 조작하고 싶을 때, 또는 className을 변경하고 싶을 때  사용한다.

 

예시 

function ToggleComponet() {
  const myRef = useRef(null);

  const toggleClassName = () => {
    if (myRef.current.classList.contains("second")) {
      myRef.current.classList.remove("second");
    } else {
      myRef.current.classList.add("second");
    }
  };

  return (
    <div>
      <div ref={myRef} className="first">
        test ref 
      </div>
      <button onClick={toggleClassName}>Toggle</button>
    </div>
  );
}

버튼을 클릭하는경우 "first" -> "first second" -> "first"로 변경된다.

 

 

다만 js의 돔 조작 방식처럼 내부에 새로운 요소(div등)을 추가하는 등 랜더링을 필요로 하는 작업들은 useState를 통해 처리하는것이 적절하다. 

 

createRef 는 useRef를 사용할 수 없는 클래스형 컴포넌트에서 사용된다 useRef와는 다르게 랜더링될때마다 다시 생성되어 이전의 값을 기억하지 않는다.

 

 

https://react.dev/reference/react/forwardRef

 

forwardRef – React

The library for web and native user interfaces

react.dev

forwardRef 는 기억해두는것이 좋다 ~.~ 

 

부모컴포넌트가 자식컴포넌트의 ref를 제어해야할 때 사용한다. 

보통 리액트의 데이터흐름은 자식 -> 부모 순이지만 가~끔 이렇게 부모요소가 자식요소를 제어해야하는 경우가 발생한다. 

그럴때 사용하는것이 forwardRef이다.

(위 페이지의 예제를 가져옴)

 

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  const { label, ...otherProps } = props;
  return (
    <label>
      {label}
      <input {...otherProps} ref={ref} />
    </label>
  );
});

 

자식요소인 MyInput을 forwardRef 컴포넌트로 선언하면 

 

import { useRef } from 'react';
import MyInput from './MyInput.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

부모요소인 Form에서 선언한 ref는 부모가 아닌 자식컴포넌트인 MyInput을 가리키게된다. 

따라서 핸들클릭을 통해 이벤트가 발생하면 자식요소인 input에 focus가 발생한다. 

728x90

플러터에서 웹뷰를 띄우기 위해서는, 플러터에서 기본적으로 제공하는 웹 뷰 패키지를 사용하면 된다. 

 

https://pub.dev/packages/webview_flutter/install

 

webview_flutter install | Flutter package

A Flutter plugin that provides a WebView widget backed by the system webview.

pub.dev

 

공식 페이지 설명에 따라 pub 명령어를 통해 웹뷰 패키지를 설치하고 

 

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_flutter_android/webview_flutter_android.dart';

 

상단에 패키지를 import 하면 된다. 

 

안드로이드 스튜디오 연결 등 귀찮은 설정들을 다해주고나서 공식 페이지의 예제 탭을 눌러보면

코드가 너무 길다... 나같은 플러터 초보는 코드가 한 눈에 안들어오고 그냥 눈 앞이 아득해진다. 

 

 

그래서 내가 간단히 기본 홈 위젯 바디안에 웹뷰를 띄우는 코드를 작성해봤다.

 

class WebViewScreen extends StatefulWidget {
  final String url; // 웹뷰에 로드할 URL
  const WebViewScreen({super.key, required this.url});

  @override
  State<WebViewScreen> createState() => _WebViewScreenState();
}

class _WebViewScreenState extends State<WebViewScreen> {
  late final WebViewController _controller;

  @override
  void initState() {
    super.initState();

    // 각 플랫폼(Android/iOS)에 맞는 WebViewController 생성에 필요한 초기화 파라미터 설정
    const PlatformWebViewControllerCreationParams params =
        PlatformWebViewControllerCreationParams();

    // WebViewController 초기화
    final WebViewController controller =
        WebViewController.fromPlatformCreationParams(params);

    // JavaScript 활성화 및 초기 URL 로드
    controller
      ..setJavaScriptMode(JavaScriptMode.unrestricted) // JavaScript 활성화
      ..loadRequest(Uri.parse(widget.url)); // URL 로드

    // 플랫폼이 Android인 경우 필요한 설정
    if (controller.platform is AndroidWebViewController) {
      (controller.platform as AndroidWebViewController)
          .setMediaPlaybackRequiresUserGesture(false);
    }

   
    _controller = controller;
  }

  @override
  Widget build(BuildContext context) {
    // WebView 위젯에 컨트롤러를 전달하여 UI 렌더링
    return WebViewWidget(controller: _controller);
  }
}

 

WebViewScreen의 state에서 리턴해주는 WebViewWidget은 우리가 설치한 웹뷰 패키지에서 제공하는 위젯이다.

이 위젯은 자바스크립트를 제어하고, url을 가져오는 컨트롤러를 필요로 하며, 그 컨트롤러를 웹뷰 위젯의 state에서 정의하는 것이다. 

 

 

이렇게 작성한 위젯은

 

class WebViewApp extends StatelessWidget {
  const WebViewApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('안드로이드 웹뷰 테스트'),
        ),
        body: const WebViewScreen(
          url: 'https://house-of-ham.tistory.com/',
        ),
      ),
    );
  }
}

 

파라미터로 원하는 url을 넣어 간단하게 사용할 수 있다. 

 

화면에서 보면 이런 모습. 

상단 앱바 아래에 웹뷰가 떠 있다. 

 

 

 

https://github.com/goeunleee/webview_flutter

 

GitHub - goeunleee/webview_flutter: 플러터 웹뷰 테스트 (현재는 안드로이드만 가능)

플러터 웹뷰 테스트 (현재는 안드로이드만 가능). Contribute to goeunleee/webview_flutter development by creating an account on GitHub.

github.com

 

내 레포지토리에 들어가면 main.dart에 적용한 모습 & 거기다 버튼에 따라 웹뷰 url을 변경하는 토글 기능까지 추가한 전체 코드를 볼 수 있다. (아래사진참조)

 

 

버튼에 따라 깃허브, 티스토리블로그를 변경해가며 볼 수 있는 웹 뷰 

728x90

https://ko.vite.dev/guide/

 

Vite

Vite, 프런트엔드 개발의 새로운 기준

ko.vite.dev

 

Vite는 

성능에 집중한 번들러로

 

개발 환경에서 코드를 변경할 때 parcel, webapack에 비해 빠른 속도를 자랑한다. 

 

이유는 vite의 코드 갱신 방식이 다른 번들러들과 다르기 때문인데, 다른 번들러들은 코드의 업데이트가 있을 때 전체 코드를 다시 번들링 하지만, vite는 브라우저의 요청에 따라 변경된 모듈만 전달하는 방식으로 화면을 갱신하기 때문에,

프로젝트의 코드 크기가 커질수록 갱신 속도가 느려지는 다른 번들러들에 비해, vite는 빠른 속도를 유지할 수 있다는 것이다. ~.~ 

 

또한 배포시에도 ESbuild(트랜스파일러), Rollup(트리셰이킹, 불필요한 코드를 쳐내고 번들로 합침)을 기반으로 동작하기때문에, 웹팩과 같이 증분 빌드를 지원하지는 않지만 빌드 속도 또한 매우 빠르다.

 

 

 

 

리액트 typescript 프로젝트를 vite 기반으로 생성하는 npm 명령어 

npm create vite@latest my-project-name --template react-ts

 

일할때는 webpack만을 사용해봤는데 개인적으로 공부할때 vite를 써보니 확실히 기본 설정도 간단하고 로드 속도도 빨라서 편하다고 느껴졌다. 

728x90

https://school.programmers.co.kr/learn/courses/30/lessons/12909

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 
 

function solution(s){
    var answer = true;
    var stack = [];
    
    for(i of s){
        if(i==="(") stack.push(i);
        else{
            if(stack===0)return false;
            else stack.pop(); //stack에는 (만 들어가기때문
        }
    }
    

    return stack.length===0;
}

 
처음엔 스택의 LIFO 개념을 생각해서 push pop을 이용해 이렇게 작성했다. 
그러나 2,6번 케이스에서 오답이 나와서 반례를 찾아헤맸다.....
 
근데 완전 바보짓이었음 헤헷 
stack === 0 이라고 써놔서 오답이 뜬거였다.
 

function solution(s){
    var answer = true;
    var stack = [];
    
    for(i of s){
        if(i==="(") stack.push(i);
        else{
            if(stack.length===0)return false;
            else stack.pop(); //stack에는 (만 들어가기때문
        }
    }
    

    return stack.length===0;
}

 
이렇게 풀면 된다 ~.~ 요즘 스택에대해 공부중이어서 냅다 풀어본 문제 

728x90

 

 

실행 문맥(excution context)이란 코드가 실행되는 동안 필요한 정보를 담고 있는 환경을 말한다. 

함수 선언-실행 과정에서 실행 문맥이 어떻게 생성되는지를 연관지어 생각해보면 이해하기 더 쉽다. 

 

 

이것으로 예시를 들면 

console.log(square(5));

function square(n) {
  return n * n;
}

 

1.  함수 선언 : 함수는 실행되기 전 변수 환경에 저장(메모리 할당)된다.

(square가 메모리에 할당됨)

 

2. 함수 호출 : 함수가 호출되는 이때 새로운 실행 문맥이 생성된다. (call stack에 저장) 또한 함수 내부의 지역변수와 매개변수가 변수 환경에 할당된다. 

(console.log로 호출하는 부분. 여기서는 호이스팅을 사용해 선언 전에 호출했다.)

 

 

함수 호출이 발생하면 새로운 실행 문맥이 생성되고,

이때 함수 내부에서 사용하는 변수와 매개변수가 변수 환경에 저장된다.

 

그다음 함수 내부의 코드가 실행되면서 렉시컬 환경을 기준으로 변수와 값을 검색하고 업데이트하는 과정을 거친다.

이 과정에서 함수 내부에서 필요한 변수는 현재 렉시컬 환경에서 찾고, 현재 환경에 없으면 상위 스코프로 올라가면서 값을 검색한다.

 

3. 함수 실행 종료 : 마지막으로, 함수 실행이 끝나면 생성되었던 실행 문맥이 스택에서 제거되고, 함수와 관련된 변수와 메모리도 해제된다.

 

 

즉, 함수 호출 과정에서는 실행 문맥이 생성되고, 코드 실행을 관리하며, 실행이 종료되면 사라지는 흐름이 반복된다.

~,~

 

console.log(square(5)); 

function square(n) {
  return n * n;
}

위 예제와 같이 함수를 선언전에 사용하는 것을 *호이스팅이라 하고, 이경우엔 에러가 발생하지 않는다.
그러나

console.log(square(5)); //에러 발생!

const square = (n) =>{
  return n * n ; 
}

이렇게 함수를 변수로 선언하여 사용하는 경우에는 변수만 선언되고, 함수내용은 초기화 되지 않기 때문에
선언 전에 호출하면 reference error가 발생한다.

 

만약 비동기 함수의 경우에는? 우선 콜스택에 들어간 다음(실행문맥생성) 작업을 위해 콜스택에서 제거되고 webapi로 보내진다. 작업이 끝나고 큐로 이동된 비동기 함수는 동기함수의 처리가 끝나면 다시 콜스택에 들어가고 다시 새로운 실행문맥이 생성된다. 그리고 실행이 끝나면 콜스택에서 제거된다. 

728x90

+ Recent posts