묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
statelessWidget 생성시 super 부분
class _Test extends StatelessWidget { const _Test({super.key}); @override Widget build(BuildContext context) { return const Placeholder(); } }stateless를 탭으로 눌러서 자동 생성했을 때 super.key 부분이 이렇게 생성이 되는데 이건 축약표현인가요?이경우, final VoidCallback onPressed; const _Test({required this.onPressed, key});위 코드를 작성해도 되는 것인지 아니면 강의처럼 아래코드로 작성해야하는지 궁금합니다!final VoidCallback onPressed; const _Test({required this.onPressed, Key? key}) : super(key: key);
-
미해결Slack 클론 코딩[실시간 채팅 with React]
프로젝트에 사용할 상태와 로직 관리 라이브러리 훅 사용 질문입니다.
로그인 및 인증,인가(세션,jwt둘다 사용), 게시판(이미지포함), 댓글, 소켓채팅 정도의 기능을 구현하여 테스트 코드와 docker로 띄워서 CI/CD까지 구현하려고 합니다. 상태랑 로직 관리를 useReducer, React Context API, React Query, redux, graphql 정도로 생각하고 있는데 어떤걸로 하는게 좋을까요?!
-
미해결Slack 클론 코딩[실시간 채팅 with React]
npx sequelize db:create 실패
mac os에서 mysql을 homebrew로 사용중입니다.root계정의 비밀번호 설정 완료했습니다.back 환경설정에서 문제를 겪고있습니다.cd back으로 경로 이동후npm i로 node 모듈 설치 성공후에.env파일 구성(비밀번호 맞음),npx sequelize db:create 명령어 입력시 아래와 같은 에러가 발생합니다.-- 에러메세지 시작Sequelize CLI [Node: 16.18.0, CLI: 6.6.0, ORM: 6.28.0]Loaded configuration file "config/config.js".Using environment "development".ERROR: Failed to create schema directory 'sleact' (errno: 2 - No such file or directory)-- 에러메세지 종료-- .env파일 시작COOKIE_SECRET=sleactcookieMYSQL_PASSWORD=kaadal-- .env파일 종료--config.js 시작require('dotenv').config();module.exports = { "development": { "username": "root", "password": process.env.MYSQL_PASSWORD, "database": "sleact", "host": "127.0.0.1", "dialect": "mysql" }, "test": { "username": "root", "password": process.env.MYSQL_PASSWORD, "database": "sleact", "host": "127.0.0.1", "dialect": "mysql" }, "production": { "username": "root", "password": process.env.MYSQL_PASSWORD, "database": "sleact", "host": "127.0.0.1", "dialect": "mysql" }}-- config.js 종료이상입니다.
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
DB 연결 시, password authentication failed for user 에러 해결 방법
version: "3.9" services: db: image: postgres:latest container_name: poster restart: always ports: - "5432:5432" environment: POSTGRES_USER: "${DB_USER_ID}" POSTGRES_PASSWORD: "${DB_USER_PW}" volumes: - commu:/var/lib/postgresql/data volumes: commu: {} 우선, docker compose down -v 명령어로 컨테이너를 삭제하고, data 폴더도 지워주세요.그리고 docker-compose.yml 파일에서 volumes를 저런 식으로 변경해주면 됩니다.변경 했으면 docker compose up 으로 컨테이너를 다시 생성해주면 정상적으로 연결됩니다.버전은 강의에서 나온대로 3으로 사용해도 상관 없습니다.
-
미해결[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
final 구문의 용법이 궁금합니다.
안녕하세요, 알찬 강의 잘 보고 있습니다. 이번 강의 중 질문 사항이 생겨 여쭤봅니다. final newBlackPink = blackPink.map((x){return '블랙핑크 $x';});이 구문에서, final 과 newBlackPink 사이에 변수 타입(이 경우는 Iterable)에 대한 선언이 있어야하는 것 아닌가? 생각했는데 안 써도 정의가 잘 되더라구요. final을 선언한 이상 변수의 값이 변경되지 않기 때문일까요? 그리고 이런 식으로 Iterable(Set, List 등도) 은 final 로 선언하는 것이 일반적인 용법인지 궁금합니다. 감사합니다.
-
미해결[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
Hive, Riverpop 제너레이터 호환성 문제
안녕하세요 선생님 지난번 날씨앱에서 질문 드리고 중급강의 GoRouter 까지 무지성으로 따라가고있는 수강생입니다.중급 캐시강의를 보다가 문득 든 생각이서버에서 받아온 데이터를 Hive를 사용해서 기기에 저장을 하고 휴대폰 DB에 저장된 리스트들을 Hive에 데이터가 있다면 hive에서 읽고 없다면 새로받아오는 형식으로 Riverpod을 사용해 관리하면 어떨까? 싶은 생각이 들었습니다. 이유는 서버에서 restAPI를 통해 받아온 값들은 메모리에 저장되어있으니 앱을 껏다 키거나 메모리가 초기화되면 캐쉬가 날아가는 것이 아닌가? 라는 생각이 들어서였구요(리스트나 자료들을 휴대폰 db에 저장 해놓고 꺼내쓴다면 재요청횟수가 줄어들어 비용절감이 되지 않을까? 싶은 생각) 그래서 당연히 생각만 가지고 있지 아직은 아무것도 할줄 모르지만 일단Hive와 Riverpod을 동시에 설치는 해보자는 생각에 인스톨 해봤는데요문제는 hive_generator의 버전은 analyzer 5.0과 호환되고 riverpop_generator와 json_serializable 제너레이터들은 analyzer 6.0과 호환되어 한프로젝트에 동시설치가 불가능 한 것 같습니다.analyzer6.0을 사용하는 다른 제너레이터들의 버전을 낮춰봐도 더 복잡하고 많은 에러가 뜨는데요. 1)그래서 캐쉬를 hive로 사용하는게 어떨까? 라는 생각이 맞는 생각인지? (예를들면 riverpod의 캐쉬를 사용하면 되는데 궂이 db를 캐쉬로 왜 사용하는가?)2)유용한 생각이라면 analyzer버전 호환문제를 해결해서 동시에 사용할 수 있는 방법이 있는지?3)동시 설치가 안된다면 hive외 대안이 있거나 sqflite 같은 다른sql이 대안이 되는지? (hive를 캐쉬 대용으로 사용하려고한것은 hive의 빠른속도 때문인데, sqflite를 사용하게되면 궂이 물리적인 db캐쉬가 유효/유용 한건지?) 에 대한 의문이 들었습니다.이런생각을 하는 이유는, 나중에 혼자 앱을 만들게 된다면 서버를 사용하는 것보다 파이어베이스 같은 클라우드 플랫폼을 사용하지 않을까 싶은 생각에서 트래픽을 줄일 수 있는 방법이 궁금해서 였습니다.아직 멋모르고 강의 열심히 따라가는 중이라 급한내용은 아니니 시간 편하실때 천천히 답변주시면 감사하겠습니다.
-
미해결[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
'circle' 부분에서 질문
안녕하세요._CustomGoogleMap에서 circle를 전달 받을때circles: Set.from([circle],)이런식으로 Set을 하셨는데, 따로 이렇게 하신 이유가 있을까요? {circle} 이렇게 하는게 더 낫지 않나 싶어서요. 감사합니다
-
미해결[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
섹션 21. 캘린더 스케쥴러 오류 문의드립니다.
색상 상태관리 부분을 듣고 있습니다. 똑 같이 따라했는데요. 아래 내용과 같이 FutureBuilder를 사용하여 똑같이 따라 했는데도 불구하고 null이 리턴되어 오류가 납니다. future: GetIt.I<LocalDatabase>().getCategoryColors() 에서 강사님 강의에서는 데이터를 가지고 오는데, 제가 만든 코드에서는 null이 리턴되네요. 혹시 몰라 main.dart에서 GetIt.I<LocalDatabase>().getCategoryColors() 를 실행해 보고, 결과를 보면 정상적인 데이터가 들어옵니다.뭐가 문제일까요?main.dartimport 'package:calendar_schedule_exam/database/drift_database.dart'; import 'package:calendar_schedule_exam/screen/home_screen.dart'; import 'package:drift/drift.dart'; import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:intl/date_symbol_data_local.dart'; const DEFAULT_COLORS = [ // 빨강 'F44336', // 주황 'FF9800', // 노랑 'FFEB3B', // 초록 'FCAF50', // 파랑 '2196F3', // 남 '3F51B5', // 보라 '9C27B0', ]; void main() async { WidgetsFlutterBinding.ensureInitialized(); await initializeDateFormatting(); final database = LocalDatabase(); GetIt.I.registerSingleton<LocalDatabase>(database); final result = GetIt.I<LocalDatabase>().getCategoryColors(); final colors = await database.getCategoryColors(); if (colors.isEmpty) { for (String hexCode in DEFAULT_COLORS) { await database.createCategoryColor( CategoryColorsCompanion( hexCode: Value(hexCode), ), ); } } runApp(MaterialApp( theme: ThemeData( fontFamily: 'NotoSans', ), home: HomeScreen(), )); } schedule_bottom_sheet.dartimport 'package:calendar_schedule_exam/component/custom_text_field.dart'; import 'package:calendar_schedule_exam/const/colors.dart'; import 'package:calendar_schedule_exam/database/drift_database.dart'; import 'package:calendar_schedule_exam/model/category_color.dart'; import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; class ScheduleBottomSheet extends StatefulWidget { const ScheduleBottomSheet({super.key}); @override State<ScheduleBottomSheet> createState() => _ScheduleBottomSheetState(); } class _ScheduleBottomSheetState extends State<ScheduleBottomSheet> { final GlobalKey<FormState> formKey = GlobalKey(); int? startTime; int? endTime; String? content; int? selectedColorId; @override Widget build(BuildContext context) { final bottomInset = MediaQuery.of(context).viewInsets.bottom; return SafeArea( bottom: true, child: GestureDetector( onTap: () => FocusScope.of(context).requestFocus(FocusNode()), child: Container( height: MediaQuery.of(context).size.height / 2 + bottomInset, color: Colors.white, child: Padding( padding: EdgeInsets.only(bottom: bottomInset), child: Padding( padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 16.0), child: Form( key: formKey, autovalidateMode: AutovalidateMode.always, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _Time( onStartSaved: (newValue) { startTime = int.parse(newValue!); }, onEndSaved: (newValue) { endTime = int.parse(newValue!); }, ), SizedBox(height: 16.0), _Content( onSaved: (newValue) { content = newValue; }, ), SizedBox(height: 16.0), FutureBuilder<List<CategoryColor>>( future: GetIt.I<LocalDatabase>().getCategoryColors(), builder: (context, snapshot) { print(snapshot.data); if (snapshot.hasData && selectedColorId == null && snapshot.data!.isNotEmpty) { selectedColorId = snapshot.data![0].id; } return _ColorPicker( colors: snapshot.hasData ? snapshot.data! : [], selectColorId: selectedColorId!, ); }), SizedBox(height: 16.0), _SaveButton( onPressed: onSavePressed, ), ], ), ), ), ), ), ), ); } void onSavePressed() { if (formKey.currentState == null) { return; } if (formKey.currentState!.validate()) { formKey.currentState!.save(); } else { print('에러가 있습니다.'); } } } class _Time extends StatelessWidget { final FormFieldSetter<String> onStartSaved; final FormFieldSetter<String> onEndSaved; const _Time( {super.key, required this.onStartSaved, required this.onEndSaved}); @override Widget build(BuildContext context) { return Row( children: [ Expanded( child: CustomTextField( label: '시작 시간', isTime: true, onSaved: onStartSaved, )), SizedBox(width: 16.0), Expanded( child: CustomTextField( label: '마감 시간', isTime: true, onSaved: onEndSaved, ), ), ], ); } } class _Content extends StatelessWidget { final FormFieldSetter<String> onSaved; const _Content({super.key, required this.onSaved}); @override Widget build(BuildContext context) { return Expanded( child: CustomTextField( label: '내용', isTime: false, onSaved: onSaved, ), ); } } class _ColorPicker extends StatelessWidget { final List<CategoryColor> colors; final int selectColorId; const _ColorPicker( {super.key, required this.colors, required this.selectColorId}); @override Widget build(BuildContext context) { return Wrap( spacing: 8.0, runSpacing: 10.0, children: colors.map((e) => rendColor(e, selectColorId == e.id)).toList(), ); } Widget rendColor(CategoryColor color, bool isSelected) { return Container( decoration: BoxDecoration( shape: BoxShape.circle, color: Color(int.parse('FF${color.hexCode}', radix: 16)), border: isSelected ? Border.all(color: Colors.black, width: 1.0) : null, ), height: 32, width: 32); } } class _SaveButton extends StatelessWidget { final VoidCallback onPressed; const _SaveButton({super.key, required this.onPressed}); @override Widget build(BuildContext context) { return SizedBox( width: double.infinity, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: PRIMARY_COLOR, ), onPressed: onPressed, child: Text('Save'), ), ); } } drift_database.dartimport 'dart:io'; import 'package:calendar_schedule_exam/model/category_color.dart'; import 'package:calendar_schedule_exam/model/schedule.dart'; import 'package:drift/drift.dart'; import 'package:drift/native.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; part 'drift_database.g.dart'; @DriftDatabase( tables: [ Schedules, CategoryColors, ], ) class LocalDatabase extends _$LocalDatabase { LocalDatabase() : super(_openConnection()); // insert schedules Future<int> createSchedule(SchedulesCompanion data) => into(schedules).insert(data); // insert categoryColors Future<int> createCategoryColor(CategoryColorsCompanion data) => into(categoryColors).insert(data); // select all categoryColors Future<List<CategoryColor>> getCategoryColors() => select(categoryColors).get(); @override // TODO: implement schemaVersion int get schemaVersion => 1; } LazyDatabase _openConnection() { return LazyDatabase(() async { final dbFolder = await getApplicationDocumentsDirectory(); final file = File(p.join(dbFolder.path, 'db.sqlite')); return NativeDatabase(file); }); }오류내용Launching lib/main.dart on iPhone 14 Pro Max in debug mode... Running Xcode build... Xcode build done. 7.8s [VERBOSE-2:FlutterDarwinContextMetalImpeller.mm(35)] Using the Impeller rendering backend. Debug service listening on ws://127.0.0.1:61505/HeTRp4ZQphA=/ws Syncing files to device iPhone 14 Pro Max... flutter: null ======== Exception caught by widgets library ======================================================= The following _TypeError was thrown building FutureBuilder<List<CategoryColor>>(dirty, state: _FutureBuilderState<List<CategoryColor>>#41541): Null check operator used on a null value The relevant error-causing widget was: FutureBuilder<List<CategoryColor>> FutureBuilder:file:///Users/choiwooin/dev/flutterProject/calendar_schedule_exam/lib/component/schedule_bottom_sheet.dart:57:21 When the exception was thrown, this was the stack: #0 _ScheduleBottomSheetState.build.<anonymous closure> (package:calendar_schedule_exam/component/schedule_bottom_sheet.dart:68:59) #1 _FutureBuilderState.build (package:flutter/src/widgets/async.dart:612:55) #2 StatefulElement.build (package:flutter/src/widgets/framework.dart:5198:27) #3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5086:15) #4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5251:11) #5 Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7) #6 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:5068:5) #7 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5242:11)
-
해결됨[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
섹션18 영상통화 HomeScreen설계 중 에러
Error: unable to locate asset entry in pubspec.yaml: "asset/NotoSansKR-Black.otf".Target debug_android_application failed: Exception: Failed to bundle asset files.FAILURE: Build failed with an exception.* Where:Script 'C:\flutter\packages\flutter_tools\gradle\flutter.gradle' line: 1201* What went wrong:Execution failed for task ':app:compileFlutterBuildDebug'.> Process 'command 'C:\flutter\bin\flutter.bat'' finished with non-zero exit value 1* Try:> Run with --stacktrace option to get the stack trace.> Run with --info or --debug option to get more log output.> Run with --scan to get full insights.* Get more help at https://help.gradle.orgBUILD FAILED in 12sException: Gradle task assembleDebug failed with exit code 1
-
미해결[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
asset폴더 통채로 옮기는건 맥에서만 가능한 기능인가요
asset폴더 통채로 옮기는건 맥에서만 가능한 기능인가요?windows는 직접 폴더 만들어서 넣어야 합니까
-
미해결[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
BoxDecoration 코드 정리 관련하여 질문있습니다
BoxDecoration 부분을 Row위젯과 달리 함수가 가져오는 식으로 정리해주셨는데 BoxDecroation도 위젯아닌가요? 어째서 stless를 쓰지않고 함수가 가져오는 식으로 정리한건지 궁금합니다.
-
미해결애플 웹사이트 인터랙션 클론!
페이지가 처음 로딩 되었을 때 애니메이션 처리가 되지 않는 느낌입니다
페이지가 맨 처음 로딩 되었을 때에는 애니메이션이 작동하지 않다가... 페이지 섹션을 일정 부분 진행하고 다시 윗방향 스크롤을 걸었을때 애니메이션이 정상작동을 합니다.그 후부터는 페이지의 시작 위치로 이동해서 시작부터 차근차근 내렸을때 정상작동을 합니다.원인이 잘 생각이 나지 않네요 Main.js(() => { let yOffset = 0; // window.pageYOffset 대신에 쓸 변수 let prevScrollHeight = 0; // 현재 스크롤 위치(yOffset) 보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합 let currentScene = 0; // 현재 활성화된 (눈 앞에 보고있는) 씬 (Scrollsection) let enterNewScene = false; // 새로운 Scene이 시작된 순간 true // sceneInfo는 각 scene에 대한 정보를 담고 있다. const sceneInfo = [ { // 0 type: 'sticky', heightNum: 5, // 브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-0'), messageA: document.querySelector('#scroll-section-0 .main-message.a'), messageB: document.querySelector('#scroll-section-0 .main-message.b'), messageC: document.querySelector('#scroll-section-0 .main-message.c'), messageD: document.querySelector('#scroll-section-0 .main-message.d') }, values: { messageA_opacity_in: [0, 1, { start: 0.1, end: 0.2 }], messageB_opacity_in: [0, 1, { start: 0.3, end: 0.4 }], messageC_opacity_in: [0, 1, { start: 0.5, end: 0.6 }], messageD_opacity_in: [0, 1, { start: 0.7, end: 0.8 }], messageA_translateY_in: [20, 0, { start: 0.1, end: 0.2 }], messageB_translateY_in: [20, 0, { start: 0.3, end: 0.4 }], messageC_translateY_in: [20, 0, { start: 0.5, end: 0.6 }], messageD_translateY_in: [20, 0, { start: 0.7, end: 0.8 }], messageA_opacity_out: [1, 0, { start: 0.25, end: 0.3 }], messageB_opacity_out: [1, 0, { start: 0.45, end: 0.5 }], messageC_opacity_out: [1, 0, { start: 0.65, end: 0.7 }], messageD_opacity_out: [1, 0, { start: 0.85, end: 0.9 }], messageA_translateY_out: [0, -20, { start: 0.25, end: 0.3 }], messageB_translateY_out: [0, -20, { start: 0.45, end: 0.5 }], messageC_translateY_out: [0, -20, { start: 0.65, end: 0.7 }], messageD_translateY_out: [0, -20, { start: 0.85, end: 0.9 }] } }, { // 1 type: 'normal', // heightNum: 5, // type normal에서는 필요 없음 scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-1'), content: document.querySelector('#scroll-section-1 .description') } }, { // 2 type: 'sticky', heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-2'), messageA: document.querySelector('#scroll-section-2 .a'), messageB: document.querySelector('#scroll-section-2 .b'), messageC: document.querySelector('#scroll-section-2 .c'), pinB: document.querySelector('#scroll-section-2 .b .pin'), pinC: document.querySelector('#scroll-section-2 .c .pin') }, values: { messageA_translateY_in: [20, 0, { start: 0.15, end: 0.2 }], messageB_translateY_in: [30, 0, { start: 0.5, end: 0.55 }], messageC_translateY_in: [30, 0, { start: 0.72, end: 0.77 }], messageA_opacity_in: [0, 1, { start: 0.15, end: 0.2 }], messageB_opacity_in: [0, 1, { start: 0.5, end: 0.55 }], messageC_opacity_in: [0, 1, { start: 0.72, end: 0.77 }], messageA_translateY_out: [0, -20, { start: 0.3, end: 0.35 }], messageB_translateY_out: [0, -20, { start: 0.58, end: 0.63 }], messageC_translateY_out: [0, -20, { start: 0.85, end: 0.9 }], messageA_opacity_out: [1, 0, { start: 0.3, end: 0.35 }], messageB_opacity_out: [1, 0, { start: 0.58, end: 0.63 }], messageC_opacity_out: [1, 0, { start: 0.85, end: 0.9 }], pinB_scaleY: [0.5, 1, { start: 0.5, end: 0.55 }], pinC_scaleY: [0.5, 1, { start: 0.72, end: 0.77 }], pinB_opacity_in: [0, 1, { start: 0.5, end: 0.55 }], pinC_opacity_in: [0, 1, { start: 0.72, end: 0.77 }], pinB_opacity_out: [1, 0, { start: 0.58, end: 0.63 }], pinC_opacity_out: [1, 0, { start: 0.85, end: 0.9 }] } }, { // 3 type: 'sticky', heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-3'), canvasCaption: document.querySelector('.canvas-caption') }, values: { } } ]; function setLayout() { // 각 스크롤 섹션의 높이 세팅 for (let i = 0; i < sceneInfo.length; i++) { if(sceneInfo[i].type === 'sticky') { sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; // window.innerHeight는 브라우저 창의 높이 } else if(sceneInfo[i].type === 'normal') { sceneInfo[i].scrollHeight = sceneInfo[i].objs.container.offsetHeight } sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px` } yOffset = window.scrollY; let totalScrollHeight = 0; for (let i = 0; i < sceneInfo.length; i++) { totalScrollHeight += sceneInfo[i].scrollHeight; if(totalScrollHeight >= yOffset) { // 이미지로 그려보면 이해가 쉬움 currentScene = i; break; } } document.body.setAttribute('id', `show-scene=${currentScene}`) } function calcValues (values, currentYOffset) { // currentYOffset은 현재 씬에서 얼마나 스크롤 됐는지를 의미 let rv; // 현재 씬(스크롤섹션)에서 스크롤된 범위를 비율로 구하기 const scrollHeight = sceneInfo[currentScene].scrollHeight const scrollRatio = currentYOffset / sceneInfo[currentScene].scrollHeight; if (values.length === 3) { // start ~ end 사이의 애니메이션을 실행 const partScrollStart = values[2].start * scrollHeight; const partScrollEnd = values[2].end * scrollHeight; const partScrollHeight = partScrollEnd - partScrollStart if (currentYOffset >= partScrollStart && currentYOffset <= partScrollEnd) { rv = (currentYOffset - partScrollStart) / partScrollStart * (values[1] - values[0]) + values[0]; } else if (currentYOffset < partScrollStart) { rv = values[0]; } else if (currentYOffset > partScrollEnd) { rv = values[1]; } } else { rv = scrollRatio * (values[1] - values[0]) + values[0]; } return rv; } function playAnimation() { const objs = sceneInfo[currentScene].objs; const values = sceneInfo[currentScene].values; const currentYOffset = yOffset - prevScrollHeight; const scrollHeight = sceneInfo[currentScene].scrollHeight; const scrollRatio = currentYOffset / scrollHeight; switch (currentScene) { case 0: // console.log('0 play'); if (scrollRatio <= 0.22) { // in objs.messageA.style.opacity = calcValues(values.messageA_opacity_in, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageA.style.opacity = calcValues(values.messageA_opacity_out, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.42) { // in objs.messageB.style.opacity = calcValues(values.messageB_opacity_in, currentYOffset); objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageB.style.opacity = calcValues(values.messageB_opacity_out, currentYOffset); objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.62) { // in objs.messageC.style.opacity = calcValues(values.messageC_opacity_in, currentYOffset); objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageC.style.opacity = calcValues(values.messageC_opacity_out, currentYOffset); objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.82) { // in objs.messageD.style.opacity = calcValues(values.messageD_opacity_in, currentYOffset); objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageD.style.opacity = calcValues(values.messageD_opacity_out, currentYOffset); objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_out, currentYOffset)}%, 0)`; } break; case 2: // console.log('2 play'); if (scrollRatio <= 0.32) { // in objs.messageA.style.opacity = calcValues(values.messageA_opacity_in, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_in, currentYOffset)}%, 0)`; } else { // out objs.messageA.style.opacity = calcValues(values.messageA_opacity_out, currentYOffset); objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_out, currentYOffset)}%, 0)`; } if (scrollRatio <= 0.67) { // in objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_in, currentYOffset)}%, 0)`; objs.messageB.style.opacity = calcValues(values.messageB_opacity_in, currentYOffset); objs.pinB.style.transform = `scaleY(${calcValues(values.pinB_scaleY, currentYOffset)})`; } else { // out objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_out, currentYOffset)}%, 0)`; objs.messageB.style.opacity = calcValues(values.messageB_opacity_out, currentYOffset); objs.pinB.style.transform = `scaleY(${calcValues(values.pinB_scaleY, currentYOffset)})`; } if (scrollRatio <= 0.93) { // in objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_in, currentYOffset)}%, 0)`; objs.messageC.style.opacity = calcValues(values.messageC_opacity_in, currentYOffset); objs.pinC.style.transform = `scaleY(${calcValues(values.pinC_scaleY, currentYOffset)})`; } else { // out objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_out, currentYOffset)}%, 0)`; objs.messageC.style.opacity = calcValues(values.messageC_opacity_out, currentYOffset); objs.pinC.style.transform = `scaleY(${calcValues(values.pinC_scaleY, currentYOffset)})`; } break; case 3: // console.log('3 play'); break; } } function scrollLoop() { enterNewScene = false; prevScrollHeight = 0; // PrevScrollHeight의 초기화가 함수 내에 있어야 맥북 기준 4 scene의 총합 11540이 찍히고 다음 scrollLoop() 이 실행될 때 다시 초기화 된다. (값이 누적되지 않는다.) for (let i = 0; i < currentScene; i++) { prevScrollHeight += sceneInfo[i].scrollHeight; } if(yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) { enterNewScene = true; currentScene++; document.body.setAttribute('id', `show-scene-${currentScene}`); } if(yOffset < prevScrollHeight) { enterNewScene = true; if (currentScene === 0) return; // scene 0에서 바운싱 스크롤이 일어났을 때 콘솔에 -값을 찍히지 않게 방지 currentScene--; document.body.setAttribute('id', `show-scene-${currentScene}`); } if (enterNewScene) return; playAnimation(); } window.addEventListener('scroll', () => { yOffset = window.scrollY; // window.pageYOffset은 deprecated 되었다. scrollLoop() }); // window.addEventListener('DOMContentLoaded', setLayout) window.addEventListener('load', setLayout) window.addEventListener('resize', setLayout) // 화면 크기가 바뀌었을때를 기준으로 setLayout을 정의하기 })();main.css@charset "utf-8"; html { font-family: 'Noto Sans KR' , sans-serif; font-size: 14px; } body { overflow-x: hidden; color: rgb(29, 29, 31); letter-spacing: -0.05em; background: white; } p { line-height: 1.6; } a { color: rgb(29, 29, 31); text-decoration: none; } .global-nav { position: absolute; top: 0; left: 0; width: 100%; height: 44px; padding: 0 1rem; } .local-nav { position: absolute; top: 45px; left: 0; width: 100%; height: 52px; padding: 0 1rem; border-bottom: 1px solid #ddd; } .global-nav-links, .local-nav-links { display: flex; align-items: center; max-width: 1000px; height: 100%; margin: 0 auto; /* background-color: gold; */ } .global-nav-links { justify-content: space-between; } .local-nav-links .product-name { margin-right: auto; font-size: 1.4rem; font-weight: bold; } .local-nav-links a { font-size: 0.8rem; } .local-nav-links a:not(.product-name) { margin-left: 2em; } .scroll-section { border: 3px solid red; padding-top: 50vh; } #scroll-section-0 h1 { font-size: 4rem; text-align: center; } .main-message { display: flex; align-items: center; justify-content: center; top: 35vh; margin: 5px 0; height: 3em; font-size: 2.5rem; opacity: 0; } .main-message p { font-weight: bold; text-align: center; line-height: 1.2; } .main-message small { display: block; margin-bottom: 0.5em; font-size: 1.2rem; } #scroll-section-2 .main-message { font-size: 3.5rem; } .description { max-width: 1000px; margin: 0 auto; padding: 0 1rem; font-size: 1.2rem; color: #888 } .description strong { float: left; margin-right: 0.2em; /* font-size: 3rem; */ color: rgb(29, 29, 31); } .desc-message { width: 50%; font-weight: bold; } .pin { width: 1px; height: 100px; background: rgb(29,29,31); } #scroll-section-2 .b { top: 10%; left: 40%; } #scroll-section-2 .c { top: 15%; left: 45%; } .mid-message { max-width: 1000px; margin: 0 auto; padding: 0 1rem; font-size: 2rem; color: #888; } .mid-message strong { color: rgb(29,29,31); } .canvas-caption { max-width: 1000px; margin: 0 auto; padding: 0 1rem; font-size: 1.2rem; color: #888; } .footer { display: flex; align-items: center; justify-content: center; height: 7rem; color: white; background: darkorange; } .sticky-elem { display: none; position: fixed; left: 0; width: 100%; } body#show-scene-0 #scroll-section-0 .sticky-elem, body#show-scene-1 #scroll-section-1 .sticky-elem, body#show-scene-2 #scroll-section-2 .sticky-elem, body#show-scene-3 #scroll-section-3 .sticky-elem { display: block; } @media(min-width: 1024px) { #scroll-section-0 h1 { font-size: 9vw; } .main-message { font-size: 4vw; } .description { font-size: 2rem; } .description strong { font-size: 6rem; } #scroll-section-2 .main-message { font-size: 6vw; } .main-message small { font-size: 1.5vw; } .desc-message { width: 20%; } #scroll-section-2 .b { top: 20%; left: 53%; } #scroll-section-2 .c { left: 55%; } .mid-message { font-size: 4vw; } .canvas-caption { font-size: 2rem; } }blank.html<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>AirMug Pro</title> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;900&display=swap" rel="stylesheet"> <link rel="stylesheet" href="css/default.css"> <link rel="stylesheet" href="css/main.css"> </head> <body> <div class="container"> <nav class="global-nav"> <div class="global-nav-links"> <a href="" class="global-nav-item">Rooms</a> <a href="" class="global-nav-item">Ideas</a> <a href="" class="global-nav-item">Stores</a> <a href="" class="global-nav-item">Contact</a> </div> </nav> <nav class="local-nav"> <div class="local-nav-links"> <a href="#" class="product-name">AirMug Pro</a> <a href="#">개요</a> <a href="#">제품사양</a> <a href="#">구입하기</a> </div> </nav> <section class="scroll-section" id="scroll-section-0"> <h1>AirMug Pro</h1> <div class="sticky-elem main-message a"> <p>온전히 빠져들게 하는<br>최고급 세라믹</p> </div> <div class="sticky-elem main-message b"> <p>주변 맛을 느끼게 해주는<br>주변 맛 허용 모드</p> </div> <div class="sticky-elem main-message c"> <p>온전히 편안한<br>맞춤형 손잡이</p> </div> <div class="sticky-elem main-message d"> <p>새롭게 입가를<br>찾아온 매혹</p> </div> </section> <section class="scroll-section" id="scroll-section-1"> <p class="description"> <strong>보통 스크롤 영역</strong> Lorem, ipsum dolor sit amet consectetur adipisicing elit. Dolore explicabo commodi facere nisi expedita eaque quasi doloremque eligendi natus architecto, facilis velit nobis voluptatum suscipit autem soluta debitis neque illum quibusdam similique odio maiores. Ab optio laborum eum sit praesentium, aperiam voluptatem nihil modi tempore a magni necessitatibus hic exercitationem temporibus doloremque error omnis. Sequi, repudiandae dolor eaque non soluta praesentium doloremque, doloribus, ea consequuntur sit perferendis ex! Repellendus natus harum soluta dolorem voluptas alias qui, ipsa vero! Alias voluptate libero, facilis molestias nam aspernatur, amet accusantium consequatur a nostrum temporibus. Qui obcaecati optio quod iusto totam, aut iste dolor ea. Explicabo dolorem ducimus natus, officiis aut minima, ad sapiente voluptatem repellat aliquid suscipit at. Architecto nostrum perferendis, fugiat velit perspiciatis illum dignissimos, vitae delectus magnam blanditiis omnis ratione nihil minus deserunt repudiandae necessitatibus et, tempore cupiditate sapiente? Debitis obcaecati repellendus corrupti odit incidunt deleniti exercitationem dolorum ipsum consequatur rerum maxime, corporis placeat deserunt veniam cum qui dolorem nulla fugit eius ipsam quibusdam saepe! Aut culpa excepturi perferendis, sint animi tempora. Officia necessitatibus libero impedit alias commodi excepturi accusamus molestiae modi laudantium voluptates totam earum autem eos numquam at consequuntur ipsam repellat, perspiciatis veniam? Expedita nobis ducimus quisquam accusantium! Velit! </p> </section> <section class="scroll-section" id="scroll-section-2"> <div class="sticky-elem main-message a"> <p> <small>편안한 촉감</small> 입과 하나 되다 </p> </div> <div class="sticky-elem desc-message b"> <p> 편안한 목넘김을 완성하는 디테일한 여러 구성 요소들, 우리는 이를 하나하나 새롭게 살피고 재구성하는 과정을 거쳐 새로운 수준의 머그, AirMug Pro를 만들었습니다. 입에 뭔가 댔다는 감각은 어느새 사라지고 오롯이 당신과 음료만 남게 되죠. </p> <div class="pin"></div> </div> <div class="sticky-elem desc-message c"> <p> 디자인 앤 퀄리티 오브 스웨덴,<br>메이드 인 차이나 </p> <div class="pin"></div> </div> </section> <section class="scroll-section" id="scroll-section-3"> <p class="mid-message"> <strong>Retina 머그</strong> 아이디어를 광할하게 펼칠<br> 아름답고 부드러운 음료 공간. </p> <p class="canvas-caption"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Expedita nulla nesciunt tempora asperiores culpa, illo et assumenda vel odio voluptatem? Saepe rerum qui accusamus maiores ex libero natus obcaecati accusantium aperiam eligendi ut et facere quis molestias quos repudiandae, omnis facilis deserunt cum! Expedita ipsam aliquid beatae doloremque, officia explicabo non neque reprehenderit qui quo, recusandae incidunt saepe totam! Adipisci dolorem inventore soluta. Natus pariatur sit quisquam dicta placeat! Molestias voluptas eos asperiores maxime, dolorem corporis eum quos iste dolores eveniet, cumque officiis beatae adipisci! Corporis quibusdam voluptates explicabo officiis quis cupiditate qui officia expedita. Praesentium placeat debitis recusandae ipsa similique optio accusamus, omnis vero a ducimus blanditiis fugit asperiores maiores mollitia? Pariatur exercitationem culpa, fugiat accusantium soluta incidunt beatae debitis laborum, harum neque aliquam, asperiores quod! Error numquam voluptate aut corrupti nam! Iusto illum accusamus sapiente asperiores quo eum perspiciatis aliquam blanditiis dolorem natus, modi magnam molestiae accusantium porro quisquam iure tempora laborum deserunt recusandae omnis dolore eos pariatur eveniet. Exercitationem mollitia inventore at quasi aperiam blanditiis minima accusantium, delectus, possimus quidem labore sapiente eius sint molestias doloribus est autem cupiditate iure veniam optio! Placeat dolor et, eos, saepe nemo tempore aliquid alias, facere pariatur optio sint nam ea. </p> </section> <footer class="footer"> 2023-08-01 Jun </footer> </div> <script src="js/main.js"></script> </body> </html>
-
미해결[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
플러터 코드푸쉬
좋은 강의 잘 수강했습니다!플러터를 처음 시작할 때 장단점에 대한 부분에서 코드푸쉬와 앱 업데이트 관련 부분이 플러터의 약점이라고 들었는데, 앱 내부에 조금의 변화라도 있으려면 마켓 승인을 다시 받아야 한다는 뜻인가요?작은 예시로, 배달의 민족 앱을 플러터로 제작하고, 새로운 가게들을 추가하려면, 그 때 마다 외부 데이터베이스와의 연동으로 해결할 수 있는 문제인지, 아니면 마켓에 일일이 승인을 받아야 하는건가요?
-
미해결Flutter + Firebase로 넷플릭스 UI 클론 코딩하기 [무작정 플러터]
firebase 설치이후 디버깅하면 아래와 같은 에러가 발생하는데 알려주실분 계실까요?
code build done. 540.2sFailed to build iOS appError (Xcode): redefinition of module 'Firebase'/Users/hoon/development/flutter_Test_Pj1/hoon_nexflex/flutter_application_1/ios/Pods/Firebase/CoreOnly/Sources/module.modulemap:0:7Error (Xcode): could not build module 'Flutter'/Users/hoon/development/flutter_Test_Pj1/hoon_nexflex/flutter_application_1/ios/Runner/GeneratedPluginRegistrant.h:9:8Error (Xcode): failed to emit precompiled header'/Users/hoon/Library/Developer/Xcode/DerivedData/Runner-awozdwnprnvtdrcrfughfazvzhji/Build/Intermediates.noindex/PrecompiledHeaders/Runner-Bridging-Header-swift_1TK2KTDMIUV5Y-clang_2JGQSHWY4RAHY.pch' for bridging header'/Users/hoon/development/flutter_Test_Pj1/hoon_nexflex/flutter_application_1/ios/Runner/Runner-Bridging-Header.h'Could not build the application for the simulator.Error launching application on iPhone 13.
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
NextJS 강의 순서 질문입니다.
안녕하세요. 레딧사이트 프로젝트를 진행하기 앞서 올려주신 NextJS 강의를 먼저 들으려고 합니다.강의 듣는 순서가 부록(섹션11) 먼저 듣고 섹션10(13버전)을 들으면 될까요?아니면 섹션10만 들어도 괜찮을까요?
-
미해결[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
SliverAppBar -> flexibleSpace에서 백그라운드 이미지 페이드아웃
안녕하세요 잘 보고 있습니다!! 플러터만세..CustomScrollView - SliverAppBar 뽀개버리기 강의에서,SliverAppBar에 flexibleSpace에 background에 image를 넣어서 스크롤을 위아래로 하실때 앱바에 이미지가 페이드 아웃 페이드 인 되듯이 자연스럽게 되는데 저는 이미지가 뚝 없어졌다가 뚝 생깁니다!! 따로 설정을 해주어야 하나요?
-
해결됨[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
Package 선택 어떻게 알아요?
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.코드팩토리 디스코드https://bit.ly/3HzRzUMFlutter 강의를 구매하시면 코드팩토리 디스코드 서버 플러터 프리미엄 채널에 들어오실 수 있습니다! 디스코드 서버에 들어오시고 저에게 메세지로 강의를 구매하신 이메일을 보내주시면 프리미엄 채널에 등록해드려요! 프리미엄 채널에 들어오시면 모든 질의응답 최우선으로 답변해드립니다! ===================================프로젝트를 만들때 어떤 종류의 pub.dev package 들이 사용되는지 어떻게 알 수 있나요? 구글링인가요?
-
해결됨[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
calendar 프로젝트에서 저장한 데이터 백업하는 방법
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.코드팩토리 디스코드https://bit.ly/3HzRzUMFlutter 강의를 구매하시면 코드팩토리 디스코드 서버 플러터 프리미엄 채널에 들어오실 수 있습니다! 디스코드 서버에 들어오시고 저에게 메세지로 강의를 구매하신 이메일을 보내주시면 프리미엄 채널에 등록해드려요! 프리미엄 채널에 들어오시면 모든 질의응답 최우선으로 답변해드립니다! calendar 프로젝트에서 앱으로 로컬에 저장한 데이터들을 백업해서 따로 파일로 보관하는 인터페이스를 앱에 구현하고싶은데,어떤식으로 구현해야하나요?
-
해결됨[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
IOS Simulator로 run 시도 시 에러가 발생합니다.
프로젝트 기본 세팅(home_screen.dart 추가 및 widget 생성) 이후 asset을 추가하고video_player와 image_picker의 dependency를 추가한 다음 pub get을 실행한 뒤info.plist에 image_picker 관련 권한을 추가하여 영상에서 나온 것 처럼 세팅을 마치고run을 실행하니 아래 사진과 같은 에러가 발생합니다.flutter doctor 를 실행했을 때 이상이 없는 것을 보아 프로젝트 세팅이 잘못 된 것 같은데, 혹시 어떤 부분이 잘못되었을까요?
-
해결됨[2024 최신] [코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
터미널 명령어 문의
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.코드팩토리 디스코드https://bit.ly/3HzRzUMFlutter 강의를 구매하시면 코드팩토리 디스코드 서버 플러터 프리미엄 채널에 들어오실 수 있습니다! 디스코드 서버에 들어오시고 저에게 메세지로 강의를 구매하신 이메일을 보내주시면 프리미엄 채널에 등록해드려요! 프리미엄 채널에 들어오시면 모든 질의응답 최우선으로 답변해드립니다!MacBookAir make_calendar % flutter pub run build_runner buildDeprecated. Use dart run instead.Could not find package "build_runner". Did you forget to add a dependency?위 명령어 입력했더니 Deprecated되어서pub.dev에서 builder_runner를 yaml파일에 추가후 다 시 진행하니까실행은 되는데[INFO] Succeeded after 19ms with 0 outputs (0 actions)이렇게 아웃풋 파일이 안생기네요.. 어떻게 해야하나요?